Eureka核心源码解析(3)- 服务发现、集群信息同步篇

696 阅读4分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金。


在前面的两篇文章中,我们介绍了Eureka的服务的注册与续约服务剔除与下线,本文我们再来看一看最后两个模块,服务发现集群信息同步

服务发现

Eureka-client

在学习服务发现的源码前,先写一个测试用例:

@Autowired
private DiscoveryClient discoveryClient;

@GetMapping("/find")
public void test(String id){
    List<ServiceInstance> instances = discoveryClient.getInstances(id);
    System.out.println(instances);
}

调用DiscoveryClientgetInstances方法,可以根据服务id获取服务实例列表:

图片

那么这里就有一个问题了,我们还没有去调用微服务,那么服务列表是什么时候被拉取或缓存到本地的服务列表的呢?答案是在这里调用了CompositeDiscoveryClientgetInstances()方法:

图片

中间调用过程省略:

EurekaDiscoveryClient # getInstances() ->
DiscoveryClient # getInstancesByVipAddress() ->
                # getInstancesByVipAddress() ->  //和上面不是一个方法
Applications # getInstancesByVirtualHostName()

查看Applications中的getInstancesByVirtualHostName方法:

图片

发现一个名为virtualHostNameAppMap的Map集合中已经保存了当前所有注册到eureka的服务列表。

private final Map<String, VipIndexSupport> virtualHostNameAppMap;

也就是说,在我们没有手动去调用服务的时候,该集合里面已经有值了,说明在Eureka-server项目启动后,会自动去拉取服务,并将拉取的服务缓存起来。

那么追根溯源,来查找一下服务的发现究竟是什么时候完成的。回到DiscoveryClient这个类,在它的构造方法中定义了任务调度线程池cacheRefreshExecutor,定义完成后,调用initScheduledTask方法:

图片

在这个thread中,调用了refreshRegistry()方法:

图片

fetchRegistry方法中,执行真正的服务列表拉取:

图片

fetchRegistry方法中,先判断是进行增量拉取还是全量拉取:

1、全量拉取

当缓存为null,或里面的数据为空,或强制时,进行全量拉取,执行getAndStoreFullRegistry方法:

图片

2、增量拉取

只拉取修改的,执行getAndUpdateDelta方法:

图片

①②:先发送http请求,获取在eureka-server中修改或新增的集合

③:判断,若拉取的集合为null,则进行全量拉取

④:更新操作,在updateDelta方法中,根据类型进行更改

图片

⑤:获取一致性的hashcode值,用来校验eureka-server集合和本地是否一样

图片

在这进行判断,若远程集合的hash值等于缓存中的hash值,不需要拉取,否则再进行拉取一次。

最后提一下,Applications中定义的以下这些变量,都是在eureka-server中准备好的,直接拉取就可以了。

private final AbstractQueue<Application> applications;
private final Map<String, Application> appNameApplicationMap;
private final Map<String, VipIndexSupport> virtualHostNameAppMap;
private final Map<String, VipIndexSupport> secureVirtualHostNameAppMap;

对服务发现过程进行一下重点总结:

  • 服务列表的拉取并不是在服务调用的时候才拉取,而是在项目启动的时候就有定时任务去拉取了,这点在DiscoveryClient的构造方法中能够体现;
  • 服务的实例并不是实时的Eureka-server中的数据,而是一个本地缓存的数据;
  • 缓存更新根据实际需求分为全量拉取与增量拉取。

集群信息同步

Eureka-server

集群信息同步发生在Eureka-server之间,之前提到在PeerAwareInstanceRegistryImpl类中,在执行register方法注册微服务实例完成后,执行了集群信息同步方法replicateToPeers,具体分析一下该方法:

图片

首先,遍历集群节点,用以给各个集群信息节点进行信息同步。

然后,调用replicateInstanceActionsToPeers方法,在该方法中根据具体的操作类型Action,选择分支,最终调用PeerEurekaNoderegister方法:

图片

最终发送http请求,但是与普通注册操作不同的时,这时将集群同步的标识置为true,说明注册信息是来自集群同步。

图片

在注册过程中运行到addInstance方法时,单独注册时isReplication的值为false,集群同步时为true。通过该值,能够避免集群间出现死循环,进行循环同步的问题。

最后

到这里,Eureka声明周期中比较重要的六个部分我们就讲完了。由于篇幅有限,只能讲一下大致的流程,如果您还想再深入了解一些,不妨自己看看源码,毕竟,源码时最好的老师。

如果觉得对您有所帮助,小伙伴们可以点赞、转发一下,非常感谢。

公众号码农参上,加个好友,做个点赞之交啊