Eureka源码分析--自我保护机制(6)
前言
在eureka server启动定时剔除失效服务的时候,有一个isLeaseExpirationEnabled() 的调用,当这个调用返回为false的时候,是不会进行剔除失效的实例。这个机制就是eureka server自我保护机制。
isLeaseExpirationEnabled()
public boolean isLeaseExpirationEnabled() {
//这里是看看有没有开启自我保护机制,默认是开启的
if (!isSelfPreservationModeEnabled()) {
// The self preservation mode is disabled, hence allowing the instances to expire.
return true;
}
//判断依据 当每分钟的阈值大于0并且 最后一次收到的心跳总和数大于阈值的情况下不会开启自我保护
return numberOfRenewsPerMinThreshold > 0 && getNumOfRenewsInLastMin() > numberOfRenewsPerMinThreshold;
}
其实这里的自我保护机制还是比较简单的,当实例剔除以后是会重新计算一下每分钟的阈值,一般情况下阈值都会大于0,在每次进行剔除服务前,会在将上一次收到心跳的总数和阈值进行比较,如果上一分钟server出现故障,那么此时这个值一定会小于阈值的,也就会触发自我保护的机制。
上一分钟的心跳是怎么统计的
在接收心跳的方法中存在一个renewsLastMin.increment();方法,而这个方法中采用了一个cas无锁化操作将数值进行累加,但是看到这里
//AbstractInstanceRegistry.renew(String appName, String id, boolean isReplication)
renewsLastMin.increment();
//MeasuredRate.increment()
public void increment() {
currentBucket.incrementAndGet();
}
其实是有一个问题的,那么就是这个值在一直累加,但是我需要的是每分钟的统计,而不是总数,是不是有个个地方应该按照分钟将这个值清零。随后看了一下currentBucket这个变量的调用,发现在当前类下有一个清零的操作
public synchronized void start() {
if (!isActive) {
timer.schedule(new TimerTask() {
@Override
public void run() {
try {
// Zero out the current bucket.
lastBucket.set(currentBucket.getAndSet(0));
} catch (Throwable e) {
logger.error("Cannot reset the Measured Rate", e);
}
}
}, sampleInterval, sampleInterval);
isActive = true;
}
}
那么这个start又是在哪调用的呢?向上查找发现其实还是在初始化剔除实例操作的开头调用的。
protected void postInit() {
renewsLastMin.start();
.....
}
现在这个流程就完整了。
总结
自我保护机制和剔除实例的动作在流程上并不难,但是想要将两者完美的结合起来就需要仔细的思考,就比如如何判定服务是否需要被剔除,但是这里还存在一个问题就是有些服务实例已经下线了,但是server并没有给他剔除掉,这也就导致了其他client会在同步的时候将失效的服务一并同步到本地中去。