1. 背景
我们都知道自我保护机制,都知道15分钟,85%的心跳阈值,那么这是如何计算的?源码中怎么体现的呢?今天来研究一下~
2.进入自我保护的模式的判断
public boolean isLeaseExpirationEnabled() {
// eureka server自我保护功能是否已经过期
if (!isSelfPreservationModeEnabled()) {
// The self preservation mode is disabled, hence allowing the instances to expire.
// 自我保护模式关闭的情况下,心跳过期功能是一直能够使用的
return true;
}
// 当自我保护模式功能开启时,看看上一分钟发送的心跳数是否大于numberOfRenewsPerMinThreshold
// numberOfRenewsPerMinThreshold 就是至少应该发送这么多心跳次数,eureka server才算正常,才不会进入保护模式
// 上一分钟发送心跳次数是否大于进入自我保护的阈值
return numberOfRenewsPerMinThreshold > 0 && getNumOfRenewsInLastMin() > numberOfRenewsPerMinThreshold;
}
上面这个方法就是eureka 判断是否进入自我保护模式的方法,核心就是getNumOfRenewsInLastMin() > numberOfRenewsPerMinThreshold
如果上一分钟收到的心跳数getNumOfRenewsInLastMin
小于 每分钟应该收到的最小心跳数numberOfRenewsPerMinThreshold
,那么就进入自我保护模式。
这个时候,基于心跳的自动踢出定时任务就失效了
3.计算粗糙的核心数据expectedNumberOfRenewsPerMin与numberOfRenewsPerMinThreshold
expectedNumberOfRenewsPerMin: 每分钟应该收到的心跳数
numberOfRenewsPerMinThreshold:每分钟应该收到的最少心跳数
这两个数据是自我保护模式判断的核心。但是eureka关于这两个数据的计算比较粗糙,而且散落在代码的各地。
总共有两个地方对这个数据执行了初始化:
1.com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#openForTraffic
在eureka server初始化完成时,这个时候已经从其他eureka server 拉取到注册表。
2. com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#updateRenewalThreshold
开启了定时任务,默认每15分钟执行一次。首次启动延迟15分钟执行(因为在openForTraffic中已经初始化过了)
获取注册表中的数据
Applications apps = eurekaClient.getApplications();
int count = 0;
for (Application app : apps.getRegisteredApplications()) {
for (InstanceInfo instance : app.getInstances()) {
// 非亚马逊云的都注册了
if (this.isRegisterable(instance)) {
++count;
}
}
}
// 这里的计算就比较粗糙 使用的是默认心跳30秒,那么每分钟默认心跳数是2,那么每分钟期待的就是注册数*2
this.expectedNumberOfRenewsPerMin = count * 2;
// 0.85 * expectedNumberOfRenewsPerMin
// 上面都计算出来,这里还又计算了一遍,这里默认设定的阈值比例为0.85
this.numberOfRenewsPerMinThreshold = (int) ((count * 2) * serverConfig.getRenewalPercentThreshold());
那么这两个数据是只在这计算一次吗?
其实并不是,这里是每15分钟会计算一次的,这里的计算结果还会会在其他的行为中进行增删改查。
我们需要知道的是,自我保护机制是应对的网络故障,或客户端非自然下线的情况。 主要是针对于心跳检查。那么在这15分钟的计算周期内,如果出现注册register,自主下线cancel 这种自主的行为还是不会受到自我保护机制的影响的,该上线上线,该下线下线。
注册register
下线cancel
方法:com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#cancel
4.15分钟的由来
private void scheduleRenewalThresholdUpdateTask() {
timer.schedule(new TimerTask() {
@Override
public void run() {
updateRenewalThreshold();
}
}, serverConfig.getRenewalThresholdUpdateIntervalMs(),//启动后延迟15分钟后开始执行updateRenewalThreshold线程任务
serverConfig.getRenewalThresholdUpdateIntervalMs());// 每15分钟执行一次
}
上面就是15分钟的由来,可以通过getRenewalThresholdUpdateIntervalMs 来进行设置
这个是在DefaultEurekaServerContext初始化时开启了该定时任务
具体调用链路:
com.netflix.eureka.DefaultEurekaServerContext#initialize
→ com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#init
→ com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#scheduleRenewalThresholdUpdateTask
5.最后一分钟统计数据的由来
这个统计我想应该来自于eureka server 接收 eureka client 心跳时进行统计的。
在第一点中有关于进入保护模式的判断,getNumOfRenewsInLastMin()
就是获取上一分钟接收的心跳数。
这个由什么来进行统计的呢?是由com.netflix.eureka.registry.AbstractInstanceRegistry#renewsLastMin
来处理的,这是在创建PeerAwareInstanceRegistryImpl对象时启动的,设定的时长是1分钟重置一次。
什么时候新增的呢?
在发送心跳续约信息时处理的
com.netflix.eureka.registry.AbstractInstanceRegistry#renew
6. 自我保护何时解除
自我保护机制是动态判断的,只要renewsLastMin
统计的数量大于numberOfRenewsPerMinThreshold
就自动解除了自我保护机制了
7.自我保护机制优缺点
自我保护好处:
防止网络波动情况下,大面积服务下线导致服务瘫痪。这个网络波动主要是指的eureka client 与eureka server 或者 eureka server 与 eureka server之间。 这样可以让客户端使用自己的本地缓存继续进行服务。
自我保护的坏处:
不规范的kill服务,还有0.85这个值,其实都会造成真正的服务下线了,但是无法被剔除。是否是网络波动其实不好判断。