续租、下线等操做比较直观,实际上也不复杂。让咱们本身想一想它们大概会在服务端有什么操做。java
lastUpdateTimestamp
, 更新一下InstanceInfo的最新状态。而后调用其余同伴节点的renew接口。evictionTimestamp
,而后设置InstanceInfo为已删除。而后把lease加入到recentlyChangedQueue
中。最后调用同伴节点的cancel接口。—— 服务端确实作了差很少这些事情。所以为了避免影响其余重要事件,这里再也不继续深刻。数组
Eureka选择作一个支持AP的系统(CAP定理)。其实很好理解,当发生大量应用实例再也不renew时,服务端认为发生了网络分区。Eureka为了保证高可用,再也不踢掉已经到期的lease,从而让依赖于该服务端实例的client端仍然能正常进行服务发现(尽管存在服务实例确实挂掉的可能,即牺牲了一致性)。bash
在Spring Cloud Eureka的Home页面上面常常会见到这个警告,就是启用了自我保护机制。网络
关于这个问题网上的解释也不少了,下面这篇文章提供了几幅有用的图:ide
The Mystery of Eureka Self-Preservation编码
这篇文章有全部你想要的。 Eureka 源码解析 —— 应用实例注册发现(四)之自我保护机制spa
搬过来一些重点:code
看代码cdn
// PeerAwareInstanceRegistryImpl.java
@Override
public boolean isLeaseExpirationEnabled() {
if (!isSelfPreservationModeEnabled()) {
// The self preservation mode is disabled, hence allowing the instances to expire.
return true;
}
return numberOfRenewsPerMinThreshold > 0 && getNumOfRenewsInLastMin() > numberOfRenewsPerMinThreshold;
}
复制代码
当每分钟心跳次数(
renewsLastMin
) 小于numberOfRenewsPerMinThreshold
时,而且开启自动保护模式开关(eureka.server.enableSelfPreservation = true
) 时,触发自动保护机制,再也不自动过时租约。server
其中:
numberOfRenewsPerMinThreshold
= expectedNumberOfRenewsPerMin
* 续租百分比( eureka.server.renewalPercentThreshold
, 默认0.85 )expectedNumberOfRenewsPerMin
= 当前注册的应用实例数 x 2为何乘以 2:
默认状况下,注册的应用实例每半分钟续租一次,那么一分钟心跳两次,所以 x 2 。
这里有硬编码的状况,所以修改应用实例的续租频率会让计算不太准。不过,自我保护机制我比较怀疑它的重要性,该调还得调。
有四个地方会从新计算numberOfRenewsPerMinThreshold
、 expectedNumberOfRenewsPerMin
。
eureka.server.renewalThresholdUpdateIntervalMs
控制。清理过时lease的也是一个定时任务EvictionTask
,频率由eureka.server.evictionIntervalTimerInMs
,默认为60秒。
EvictionTask
代码class EvictionTask extends TimerTask {
@Override
public void run() {
try {
// 获取 补偿时间毫秒数
long compensationTimeMs = getCompensationTimeMs();
logger.info("Running the evict task with compensationTime {}ms", compensationTimeMs);
// 清理过时租约逻辑
evict(compensationTimeMs);
} catch (Throwable e) {
logger.error("Could not run the evict task", e);
}
}
}
复制代码
那个getCompensationTimeMs()
方法的意思是,定时器可能比设定的时间更晚触发,缘由多是GC等。假设原本要在上一次执行后60s再次触发,但由于GC在第70秒才被触发,这时去检查有没有lease有没有超期无renew不能用70s来算,而应该还用60s来算,由于在这10s的GC时间中,极可能此服务端没法处理注册请求。补偿的10s就是这个意思,就是容许实例多超期这10s。
evict(compensationTime)
又比较长,下面分段分析。
lastUpdatedTime
+ duration
+ compensationTime
,若是前者大,说明已过时。eureka.server.renewalPercentThreshold
,默认又是0.85。这个阈值仍是很高的,若是有大量实例过时,就须要分多批执行才能清理完。internalCancel
处理每一个要被清理的lease,这个方法就是前面的cancel阶段会调的。这个自动清理彷佛没有告知同伴节点,你们各作各的。可见Eureka的一致性是蛮低的。