本文中源码,来自Eureka的v1.7.x版本。代码仓:github.com/Netflix/eur…
在上一篇文章《Eureka Client注册、续期和取消注册》中,我们了解了Eureka Client注册如何实现。 请考虑一个问题:
- 当Eureka Client的状态发生变化时,比如从UP变为DOWN,如何及时同步给Eureka server?
默认续租周期是30秒,客户端在3个周期即90秒内没有续租,才会被Eureka server从注册表移除。总不能等这么久吧?
因此,需要一种更加及时的机制,让客户端将状态变化,及时同步给Eureka。这就是Eureka Client状态更新同步。
一 节点状态同步
InstanceInfoReplicator类,实现周期性刷新节点信息,有变动时向Eureka发起注册,将最新节点信息同步。
1.1 InstanceInfoReplicator类
该类的部分属性如下:
// 默认为30
private final int replicationIntervalSeconds;
// 调度线程池
private final ScheduledExecutorService scheduler;
// 限流器
private final RateLimiter rateLimiter;
// 缓冲量,默认为2
private final int burstSize;
// 每分钟执行次数,默认为4
private final int allowedRatePerMinute;
它实现了Runnable接口,run方法核心逻辑:
- 刷新节点信息(包括hostname、ip、心跳周期配置、续租过期配置、状态),有变化时设置isInstanceInfoDirty=true;
- 如果节点信息已变动,则向Eureka发起一次注册,并消除isInstanceInfoDirty标识;
- finally中提交下一次延迟任务
1.2 创建并启动
在DiscoveryClient的构造器中,会调用initScheduledTasks(),有如下代码。
构造方法中:
- 创建了仅有一个线程的调度线程池。
- replicationIntervalSeconds默认为30秒,即每次执行的间隔时间。
- 创建了限流器RateLimiter,allowedRatePerMinute默认为4。
然后调用start()方法,提交一个延迟任务。由于调用了setIsDirty(),将在run方法首次执行时,必然发起注册。
二、StatusChangeListener接口
2.1 接口功能
StatusChangeListener是ApplicationInfoManager的嵌套类,声明了状态监听方法。
StatusChangeListener对象,需要添加到ApplicationInfoManager的listeners属性,调用registerStatusChangeListener方法即可。
当调用ApplicationInfoManager.setInstanceStatus(),会触发已注册监听器的执行,同时设置InstanceInfo.isInstanceInfoDirty = true。
2.2 接口实现
在DiscoveryClient的构造方法中,对StatusChangeListener接口有一个实现:只在shouldOnDemandUpdateStatusChange为true时,才进行注册。
核心逻辑是:立即发起一次注册。
三 状态检查
onDemandUpdate方法有两处调用,其中一处是注册HealthCheckHandler时。
将在springboot整合Eureka client时,看到对HealthCheckHandler的使用。
四 总结
源码中有4个场景,会引发Eureka客户端发起注册:
- 在心跳任务启动后,将在一个心跳周期后(默认30秒)发起首次续租,eureka响应404,将引发一次注册;
- 由InstanceInfoReplicator周期检查,在节点信息发生变化时,引发一次注册。
- 调用ApplicationInfoManager#setInstanceStatus设置节点状态时,如果状态发生变化,将触发StatusChangeListener的执行,引起一次注册
- 注册HealthCheckHandler时,引起一次注册
使用以上场景,可以在客户端状态变化时,立即同步给Eureka,而不用被动地等租期过期。