Eureka源码分析--自我保护机制(6)

181 阅读2分钟

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会在同步的时候将失效的服务一并同步到本地中去。