Eureka源码探索(一)-客户端服务端的启动和负载均衡

时间:2023-03-09 04:31:57
Eureka源码探索(一)-客户端服务端的启动和负载均衡

1. Eureka源码探索(一)-客户端服务端的启动和负载均衡

1.1. 服务端

1.1.1. 找起始点

  1. 目前唯一知道的,就是启动Eureka服务需要添加注解@EnableEurekaServer,但是暂时找不到它被使用的地方
  2. 看日志,明显有打印discovery client,服务端同时也用作客户端,因为它可以相互注册,以下是自动配置类

    Eureka源码探索(一)-客户端服务端的启动和负载均衡
  3. 知道了客户端有自动配置类,可以想象服务端也应该有,找到相应的包,发现果然有

    Eureka源码探索(一)-客户端服务端的启动和负载均衡

1.1.2. 服务初始化

  1. 启动初始化

    Eureka源码探索(一)-客户端服务端的启动和负载均衡
  2. 接下来是个发布订阅方法,发布对象继承了Spring的ApplicationEvent,可以看出肯定会有订阅者接收该配置,配置内容就是我们application.properties里配置的属性,不配则是默认属性

1.1.3. @EnableEurekaServer起作用的原理

  1. 进入该注解,可以看到注解中还有个注解@Import它的作用就是将之后的类对象所对应的实例,实例化并加入spring容器管理
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EurekaServerMarkerConfiguration.class)
public @interface EnableEurekaServer { }
  1. 进入EurekaServerMarkerConfiguration类,可以看到如下,该类的作用仅仅用来标记下,为启动类判断是否需要启动EurekaServer
@Configuration
public class EurekaServerMarkerConfiguration { @Bean
public Marker eurekaServerMarkerBean() {
return new Marker();
} class Marker { } }
  1. 它具体被使用的地方如下,用一个@ConditionalOnBean表示,若存在该类Bean,则启动配置生效

Eureka源码探索(一)-客户端服务端的启动和负载均衡

1.2. 客户端

1.2.1. 调用解析过程

  1. restTemplate调用
restTemplate.getForEntity("http://eureka-server", String.class);
  1. 在不断深入后,最终处理的是一个拦截器

    Eureka源码探索(一)-客户端服务端的启动和负载均衡
  2. 拦截器进的实际处理类是LoadBalancerInterceptor

    Eureka源码探索(一)-客户端服务端的启动和负载均衡
  3. 但里面实际的负载均衡调用loadBalancer又是RibbonLoadBalancerClient

    Eureka源码探索(一)-客户端服务端的启动和负载均衡
  4. 这就真真的进了Ribbon的负载均衡调用了,至于这个RibbonLoadBalancerClient怎么注入进来的,这也简单,这里有两层关系,该LoadBalancerInterceptor如何实例化的,和RibbonLoadBalancerClient如何实例化并注入的,可以看如下图

    Eureka源码探索(一)-客户端服务端的启动和负载均衡

    Eureka源码探索(一)-客户端服务端的启动和负载均衡

    Eureka源码探索(一)-客户端服务端的启动和负载均衡
  5. 好了,不扯开去了,继续解析服务名,实际上,接下来就是RibbonLoadBalancerClient的实现了

    Eureka源码探索(一)-客户端服务端的启动和负载均衡
  6. 继续深入

    Eureka源码探索(一)-客户端服务端的启动和负载均衡
  7. 轮询核心算法

    Eureka源码探索(一)-客户端服务端的启动和负载均衡

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();
}
}
  1. 这里用了Ribbon的负载均衡轮询算法和直接调用discoveryClient实现最简单的模拟轮询算法
  2. 注意,测试discovery的时候可以需要把@LoadBalanced注掉