eurkea是一个服务发现与注册组件,它包含客户端和服务端,服务端负责管理服务的注册信息,客户端用于简化与服务端的交互。上一章分析了eureka的服务注册,这一章来分析eureka的心跳机制
参考源码:<spring-cloud.version>Hoxton.SR9</spring-cloud.version>
上一章:【SpringCloud】Eureka源码解析 上-CSDN博客
1、心跳机制
eureka的客户端会持续向服务端发送心跳,这个定时动作就是心跳机制。根据上一章的分析,我们合理推测定时任务和initScheduledTasks方法有关。查看DiscoveryClient类的initScheduledTasks方法,详情如下
private void initScheduledTasks() {
...
// 心跳定时任务,具体任务在HeartbeatThread内
this.heartbeatTask = new TimedSupervisorTask("heartbeat", this.scheduler, this.heartbeatExecutor, renewalIntervalInSecs, TimeUnit.SECONDS, expBackOffBound,
new HeartbeatThread());
this.scheduler.schedule(this.heartbeatTask, (long)renewalIntervalInSecs, TimeUnit.SECONDS);
...
}
private class HeartbeatThread implements Runnable {
private HeartbeatThread() {
}
public void run() {
// 续约
if (DiscoveryClient.this.renew()) {
DiscoveryClient.this.lastSuccessfulHeartbeatTimestamp = System.currentTimeMillis();
}
}
}
boolean renew() {
...
// 发送心跳
EurekaHttpResponse<InstanceInfo> httpResponse = this.eurekaTransport.registrationClient.sendHeartBeat(this.instanceInfo.getAppName(), this.instanceInfo.getId(), this.instanceInfo, (InstanceInfo.InstanceStatus)null);
...
}
2、剔除服务
注册列表中的服务超过一定时间没有续约,eureka服务端就会将该服务从注册列表中剔除,剔除服务同样用到了定时任务
调用链:EurekaServerInitializerConfiguration.start
-> EurekaServerBootstrap.contextInitialized
-> initEurekaServerContext
-> PeerAwareInstanceRegistryImpl.openForTraffic
-> AbstractInstanceRegistry.postInit
protected void postInit() {
...
// 定时剔除任务,具体任务在EvictionTask内
this.evictionTaskRef.set(new EvictionTask());
this.evictionTimer.schedule((TimerTask)this.evictionTaskRef.get(), this.serverConfig.getEvictionIntervalTimerInMs(), this.serverConfig.getEvictionIntervalTimerInMs());
...
}
class EvictionTask extends TimerTask {
...
public void run() {
// 具体的剔除任务
AbstractInstanceRegistry.this.evict(compensationTimeMs);
}
}
// 剔除方法
public void evict(long additionalLeaseMs) {
logger.debug("Running the evict task");
// 是否启用心跳保护机制
if (!this.isLeaseExpirationEnabled()) {
logger.debug("DS: lease expiration is currently disabled.");
} else {
...
int registrySize = (int)this.getLocalRegistrySize();
// 根据阈值计算可以被剔除的服务最大数量
int registrySizeThreshold = (int)((double)registrySize *
this.serverConfig.getRenewalPercentThreshold());
// 剔除后剩余最小数量
int evictionLimit = registrySize - registrySizeThreshold;
// 计算两者的最小值,剔除较小数量的服务实例
int toEvict = Math.min(expiredLeases.size(), evictionLimit);
if (toEvict > 0) {
for(int i = 0; i < toEvict; ++i) {
...
// 执行删除动作
this.internalCancel(appName, id, false);
...
}
}
}
3、保护机制
eureka服务端在短时间内丢失大量客户端时,会启用自我保护模式。在保护模式下,eureka服务端不会剔除服务,直到故障恢复后,eureka服务端才会退出保护模式
eureka的自我保护机制和剔除服务相关,找到AbstractInstanceRegistry类postInit服务剔除方法
// 剔除方法
public void evict(long additionalLeaseMs) {
// 是否允许剔除服务
if (!this.isLeaseExpirationEnabled()) {
...
}
}
public class PeerAwareInstanceRegistryImpl extends AbstractInstanceRegistry implements PeerAwareInstanceRegistry {
public boolean isLeaseExpirationEnabled() {
// 是否启用自我保护机制
if (!this.isSelfPreservationModeEnabled()) {
return true;
} else {
// 已启用保护机制
// 实际续约个数大于最小续约个数时,允许服务剔除
return this.numberOfRenewsPerMinThreshold > 0 && this.getNumOfRenewsInLastMin() > (long)this.numberOfRenewsPerMinThreshold;
}
}
public boolean isSelfPreservationModeEnabled() {
return this.serverConfig.shouldEnableSelfPreservation();
}
}
4、总结
eureka客户端通过定时任务向服务端发送心跳完成续约,eureka服务端会从服务列表中剔除超时未续约的服务;若出现网络故障丢失大量服务端,eureka服务端会进入自我保护模式,不再剔除服务,直到故障修复