1. Eureka源码探索(一)-客户端服务端的启动和负载均衡
1.1. 服务端
1.1.1. 找起始点
- 目前唯一知道的,就是启动Eureka服务需要添加注解
@EnableEurekaServer
,但是暂时找不到它被使用的地方 - 看日志,明显有打印discovery client,服务端同时也用作客户端,因为它可以相互注册,以下是自动配置类
- 知道了客户端有自动配置类,可以想象服务端也应该有,找到相应的包,发现果然有
1.1.2. 服务初始化
- 启动初始化
- 接下来是个发布订阅方法,发布对象继承了Spring的
ApplicationEvent
,可以看出肯定会有订阅者接收该配置,配置内容就是我们application.properties里配置的属性,不配则是默认属性
1.1.3. @EnableEurekaServer
起作用的原理
- 进入该注解,可以看到注解中还有个注解
@Import
它的作用就是将之后的类对象所对应的实例,实例化并加入spring容器管理
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EurekaServerMarkerConfiguration.class)
public @interface EnableEurekaServer {
}
- 进入
EurekaServerMarkerConfiguration
类,可以看到如下,该类的作用仅仅用来标记下,为启动类判断是否需要启动EurekaServer
@Configuration
public class EurekaServerMarkerConfiguration {
@Bean
public Marker eurekaServerMarkerBean() {
return new Marker();
}
class Marker {
}
}
- 它具体被使用的地方如下,用一个
@ConditionalOnBean
表示,若存在该类Bean,则启动配置生效
1.2. 客户端
1.2.1. 调用解析过程
- restTemplate调用
restTemplate.getForEntity("http://eureka-server", String.class);
- 在不断深入后,最终处理的是一个拦截器
- 拦截器进的实际处理类是
LoadBalancerInterceptor
- 但里面实际的负载均衡调用
loadBalancer
又是RibbonLoadBalancerClient
- 这就真真的进了Ribbon的负载均衡调用了,至于这个
RibbonLoadBalancerClient
怎么注入进来的,这也简单,这里有两层关系,该LoadBalancerInterceptor
如何实例化的,和RibbonLoadBalancerClient
如何实例化并注入的,可以看如下图
- 好了,不扯开去了,继续解析服务名,实际上,接下来就是
RibbonLoadBalancerClient
的实现了
- 继续深入
- 轮询核心算法
1.3. 模拟负载均衡调用
1.3.1. 代码直接上
@SpringBootApplication
@EnableDiscoveryClient
@RestController
@Slf4j
public class EurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientApplication.class, args);
}
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
/**
* 使用Ribbon的负载均衡
* @return
*/
@GetMapping("/")
public ResponseEntity<String> getTime(){
return restTemplate.getForEntity("http://eureka-server", String.class);
}
/**
* 模拟轮询负载的调用
* @return
*/
@GetMapping("/discovery")
public ResponseEntity<String> discovery(){
List<ServiceInstance> instances = discoveryClient.getInstances("eureka-server");
int i = incrementAndGetModule(instances.size());
return restTemplate.getForEntity(((EurekaDiscoveryClient.EurekaServiceInstance) instances.get(i)).getInstanceInfo().getHomePageUrl(), String.class);
}
private AtomicInteger nextIndex = new AtomicInteger();
private int incrementAndGetModule(int module) {
for (; ; ) {
int current = nextIndex.get();
int next = (current + 1) % module;
if (nextIndex.compareAndSet(current,next) && current < module) {
return current;
}
}
}
/**
* 加上@LoadBalanced该注解使用的Ribbon的负载均衡算法
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
- 这里用了Ribbon的负载均衡轮询算法和直接调用
discoveryClient
实现最简单的模拟轮询算法 - 注意,测试discovery的时候可以需要把
@LoadBalanced
注掉