赞
踩
注意:Eureka新旧版本情况,旧版本已经被弃用,需要(建议)使用新的版本
旧版本:spring-cloud-starter-eureka-server,spring-cloud-starter-eureka 例子如下,如果使用下面这种方法,有可能在配置其他方面报错,如Ribbon
<!-- 服务端,面对注册中心 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!-- 客户端, 面对服务提供者和服务消费者-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
新版本:spring-cloud-starter-netflix-eureka-server,spring-cloud-starter-netflix-eureka-client 推荐使用新版本
<!-- 服务端,面对注册中心,注册中心导入此依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>3.1.2</version>
</dependency>
<!-- 客户端, 面对服务提供者和服务消费者,provider和consumer导入此依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>3.1.2</version>
</dependency>
application.yaml配置如下
server:
port: 7001
#Eureka 服务端配置,(服务端,创建服务,让其他客户端(provider,consumer)可以注册服务或拿取注册服务)
eureka:
instance:
hostname: eureka7001.com #eureka服务端名称
client:
register-with-eureka: false # 是否将自己注册到Eureka服务器中,本身是服务器,无需注册
fetch-registry: false # false表示自己就是注册中心,只需要维护服务实例,无需检索服务
service-url:
# 设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个defaultZone地址
# defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ # 单机设置
#开启集群设置如下,连接其他的注册主机
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
启动类配置
package com.laoliu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer //开启Eureka服务端服务
public class EurekaServer7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer7001.class);
}
}
application.yaml配置
server: port: 8001 # mybatis 设置 mybatis: config-location: classpath:mybatis/mybatis-config.xml type-aliases-package: com.laoliu.springcloud.pojo mapper-locations: - classpath:mybatis/mapper/**/*.xml # spring相关配置 spring: application: name: springcloud-provider-dept #实例名称,服务消费者使用的rest请求,需要这个实例名称 datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/springcloud username: root password: root dbcp2: min-idle: 5 initial-size: 5 max-total: 5 max-wait-millis: 200 #eureka 客户端配置 eureka: client: service-url: # 将8001微服务发布到1台eureka集群配置中,发现在集群中的其余注册中心也可以看到,但是平时我们保险起见,都发布! defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ instance: instance-id: springcloud-provider-dept8001 prefer-ip-address: true #监控短点配置,springboot2.5以后,默认只开启health,需要手动打开其他 management: endpoints: web: exposure: include: "*" # 全部打开 #info配置 info: app.name: laoliu-springcloud company.name: www.laoliu.com build.artifactId: ${project.artifactId} build.version: ${project.version}
启动类配置
package com.laoliu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient //开启客户端服务
@EnableDiscoveryClient //扫描所有的服务提供到注册中心中
public class DeptProvider8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider8001.class);
}
}
pom.xml
<dependencies> <dependency> <groupId>com.laoliu</groupId> <artifactId>springcloud-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <version>3.1.2</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <!-- springCloud2020 版本 把Bootstrap被默认禁用, spring.config.import加入了对解密的支持。对于Config Client、Consul、Vault和Zookeeper的配置导入, 如果需要使用原来的配置引导功能, 那么需要将org.springframework.cloud:spring-cloud-starter-bootstrap依赖引入到工程中 这样才能正常使用springCloud --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bootstrap</artifactId> </dependency> </dependencies>
注意:SpringCloud 从2020 版本 把Bootstrap被默认禁用,spring.config.import加入了对解密的支持。对于Config Client、Consul、Vault和Zookeeper的配置导入,如果需要使用原来的配置引导功能,那么需要将org.springframework.cloud:spring-cloud-starter-bootstrap依赖引入到工程中这样才能正常使用springCloud
application.yaml配置(此配置集成Ribbon)
server:
port: 80
eureka:
client:
register-with-eureka: false # false 不是服务提供者,不需要注册到Eureka中
fetch-registry: true # true 消费者需要检索注册中心服务才能调用实例,否则找不到实例,调用失败,报错!!!!!
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
启动类配置
package com.laoliu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient //开启客户端
public class DeptConsumerRibbon80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumerRibbon80.class,args);
}
}
ConfigBean配置
package com.laoliu.springcloud.config; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration public class ConfigBean { @Bean @LoadBalanced //类属Ribbon,使用该注解说明让这个RestTemplate在请求时拥有客户端负载均衡的能力 public RestTemplate getRestTemplate(){ return new RestTemplate(); } }
controller(例子说明)
package com.laoliu.springcloud.controller; import com.laoliu.springcloud.pojo.Dept; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.util.List; @RestController public class DeptConsumerController { /** * 使用RestTemplate访问restful接口 * (url,requestMap,ResponseBean.class) 这三个参数分别代表 * REST请求地址,请求参数,Http响应转换 被 转换成的对象类型 */ @Autowired private RestTemplate restTemplate; // private static final String REST_URL_PREFIX = "http://localhost:8001"; 没使用Euraka时的路径 /** * 路径前缀发生改变,不在是地址,而是实例名称,就是服务提供者的实例名,作为Eureka的客户端,已经在配置文件application中配置好注册中心地址, * 所以很好理解,配合Ribbon,负载均衡,选取合适的注册中心再通过实例名称和服务路径进行服务调用,此时实例名称大小写都可以 */ private static final String REST_URL_PREFIX = "http://springcloud-provider-dept"; @PostMapping("/consumer/dept/add") public boolean add(Dept dept){ return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add",dept,Boolean.class); } @GetMapping("/consumer/dept/get/{id}") public Dept get(@PathVariable("id") Long id){ return restTemplate.getForObject(REST_URL_PREFIX + "/dept/get/"+id,Dept.class); } @GetMapping("/consumer/dept/list") public List<Dept> getAll(){ return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list",List.class); } @GetMapping("/consumer/dept/discovery") public Object discovery(){ return restTemplate.getForObject(REST_URL_PREFIX + "/dept/discovery",Object.class); } }
消费者模块所需要的Ribbon在新版的Eureka中已经被弃用(或者说是内置),不可以再导入依赖,导入spring-cloud-starter-netflix-ribbon将会找不到实例,报错!!!
读取源码可知,新版的Eureka的LoadBalanced实现了两种负载均衡的算法:轮询算法(默认)和随机算法
LoadBalancerClientConfiguration的源码有一项如下:
@ConditionalOnDiscoveryEnabled
public class LoadBalancerClientConfiguration {
****略***
@Bean
@ConditionalOnMissingBean //缺少该Bean时将会被启用,即可以自定义替换默认使用的负载均衡算法
public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty("loadbalancer.client.name");
//默认轮询算法
return new RoundRobinLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
****略***
}
在新版的Eureka中使用@LoadBalanced进行客户端请求负载均衡,默认使用的算法是轮询(即轮流调用实例中的服务),可以将算法修改为随机,示例如下:
ConfigBean
package com.laoliu.springcloud.config; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration @LoadBalancerClient(name = "SPRINGCLOUD-PROVIDER-DEPT",configuration = CustomLoadBalancerConfiguration.class)//name为服务实例名称,configuration是自定义好的算法类 public class ConfigBean { @Bean @LoadBalanced public RestTemplate getRestTemplate(){ return new RestTemplate(); } }
定义一个CustomLoadBalancerConfiguration类来替换算法
package com.laoliu.springcloud.config; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer; import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer; import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; import org.springframework.context.annotation.Bean; import org.springframework.core.env.Environment; /** *这个地方可以不加@Configuration,我的理解是@LoadBalancerClient拿到该类Class后通过反射会创建对象, *这其中的Bean将会被识别创建并替换原先的ReactorLoadBalancer,所以可以不用加,当然加上也可以运行,亲测可以。 */ public class CustomLoadBalancerConfiguration { @Bean //必须,不配置无法实现 ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory){ String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); //与源码类似,但是创建的是随机算法 return new RandomLoadBalancer( loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class) ,name); } }
通过源码可以看出,传进的CustomLoadBalancerConfiguration类会通过反射机制创建对应的CustomLoadBalancerConfiguration对象或者类中被标识为Bean的ReactorLoadBalancer,然后替换为原先的算法配置,原本我以为应该是反射后创建的对象给spring托管,但是当我用@Autowired输出验证该对象时为空,证明该对象应该不是由spring托管,继续验证@Bean,当我未加入@Bean时,算法没被替换,当我加入@Bean后,算法替换成功,但是用@Autowired输出验证ReactorLoadBalancer为空,最后我得出结论:
LoadBalancerClient源码:
@Configuration( proxyBeanMethods = false ) @Import({LoadBalancerClientConfigurationRegistrar.class}) @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface LoadBalancerClient { @AliasFor("name") String value() default ""; @AliasFor("value") String name() default ""; Class<?>[] configuration() default {}; }
LoadBalancerClientConfigurationRegistrar源码:
public class LoadBalancerClientConfigurationRegistrar implements ImportBeanDefinitionRegistrar { public LoadBalancerClientConfigurationRegistrar() { } private static String getClientName(Map<String, Object> client) { if (client == null) { return null; } else { String value = (String)client.get("value"); if (!StringUtils.hasText(value)) { value = (String)client.get("name"); } if (StringUtils.hasText(value)) { return value; } else { throw new IllegalStateException("Either 'name' or 'value' must be provided in @LoadBalancerClient"); } } } private static void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(LoadBalancerClientSpecification.class); builder.addConstructorArgValue(name); builder.addConstructorArgValue(configuration); //这个获取Bean定义,正是我考虑的由@Bean识别进行反射 registry.registerBeanDefinition(name + ".LoadBalancerClientSpecification", builder.getBeanDefinition()); } public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { Map<String, Object> attrs = metadata.getAnnotationAttributes(LoadBalancerClients.class.getName(), true); if (attrs != null && attrs.containsKey("value")) { AnnotationAttributes[] clients = (AnnotationAttributes[])((AnnotationAttributes[])attrs.get("value")); AnnotationAttributes[] var5 = clients; int var6 = clients.length; for(int var7 = 0; var7 < var6; ++var7) { AnnotationAttributes client = var5[var7]; registerClientConfiguration(registry, getClientName(client), client.get("configuration")); } } if (attrs != null && attrs.containsKey("defaultConfiguration")) { String name; if (metadata.hasEnclosingClass()) { name = "default." + metadata.getEnclosingClassName(); } else { name = "default." + metadata.getClassName(); } registerClientConfiguration(registry, name, attrs.get("defaultConfiguration")); } Map<String, Object> client = metadata.getAnnotationAttributes(LoadBalancerClient.class.getName(), true); String name = getClientName(client); if (name != null) { //拿到configuration中的Class进行配置 registerClientConfiguration(registry, name, client.get("configuration")); } } }
源码部分没有过度深究,如果有大神深入了解过源码,请求在评论区或者私聊我为我指点迷津,上文有错的地方也请各位不吝指出,我将进行改正和学习,最后感谢各位的观看!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。