小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金。
在前面的两篇文章中,我们介绍了Eureka的服务的注册与续约 和 服务剔除与下线,本文我们再来看一看最后两个模块,服务发现和集群信息同步。
服务发现
Eureka-client
在学习服务发现的源码前,先写一个测试用例:
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/find")
public void test(String id){
List<ServiceInstance> instances = discoveryClient.getInstances(id);
System.out.println(instances);
}
调用DiscoveryClient 的getInstances方法,可以根据服务id获取服务实例列表:
那么这里就有一个问题了,我们还没有去调用微服务,那么服务列表是什么时候被拉取或缓存到本地的服务列表的呢?答案是在这里调用了CompositeDiscoveryClient 的 getInstances()方法:
中间调用过程省略:
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,选择分支,最终调用PeerEurekaNode的register方法:
最终发送http请求,但是与普通注册操作不同的时,这时将集群同步的标识置为true,说明注册信息是来自集群同步。
在注册过程中运行到addInstance方法时,单独注册时isReplication的值为false,集群同步时为true。通过该值,能够避免集群间出现死循环,进行循环同步的问题。
最后
到这里,Eureka声明周期中比较重要的六个部分我们就讲完了。由于篇幅有限,只能讲一下大致的流程,如果您还想再深入了解一些,不妨自己看看源码,毕竟,源码时最好的老师。
如果觉得对您有所帮助,小伙伴们可以点赞、转发一下,非常感谢。
公众号
码农参上,加个好友,做个点赞之交啊