深入理解Eureka - Eureka自我保护机制


Eureka自我保护机制是为了防止误杀服务而提供的一个机制。

何为误杀?

理论上每个服务都有故障的可能,那么自然Eureka注册中心也有故障的可能性。如果注册中心发生故障,服务就有可能不能正常续约。而这个时候服务仍然是可以正常服务的,如果Eureka剔除任务将其剔除,则会造成误杀。

怎么判断Eureka Server故障了?

Eureka Server以一种谦虚的态度来判断是不是自己出了问题,也就是当大量的服务续约超时时,就认为是自己出现问题了。(如果少量服务续约超时,则认为是服务故障)

Eureka自我保护机制

自我保护开启条件

Eureka Server通过判断是否有大量续约失败,来确定是否开启自我保护。这里提供了一个阀值numberOfRenewsPerMinThreshold和上一分钟的续约数进行对比,如果实际的续约数小于了自我保护阀值,则开启自我保护。

    public boolean isLeaseExpirationEnabled() {
        if (!isSelfPreservationModeEnabled()) {
            // The self preservation mode is disabled, hence allowing the instances to expire.
            return true;
        }
        return numberOfRenewsPerMinThreshold > 0 && getNumOfRenewsInLastMin() > numberOfRenewsPerMinThreshold;
    }

自我保护阀值的计算

有三个地方会重新计算自我保护阀值numberOfRenewsPerMinThreshold。

1、当新服务注册(register)时

synchronized (lock) {
    if (this.expectedNumberOfClientsSendingRenews > 0) {
        // Since the client wants to register it, increase the number of clients sending renews
        this.expectedNumberOfClientsSendingRenews = this.expectedNumberOfClientsSendingRenews + 1;
        updateRenewsPerMinThreshold();
    }
}

2、当服务退出(cancel)时

    public boolean cancel(final String appName, final String id,
                          final boolean isReplication) {
        if (super.cancel(appName, id, isReplication)) {
            replicateToPeers(Action.Cancel, appName, id, null, null, isReplication);
            synchronized (lock) {
                if (this.expectedNumberOfClientsSendingRenews > 0) {
                    // Since the client wants to cancel it, reduce the number of clients to send renews
                    this.expectedNumberOfClientsSendingRenews = this.expectedNumberOfClientsSendingRenews - 1;
                    updateRenewsPerMinThreshold();
                }
            }
            return true;
        }
        return false;
    }

3、TimerTask定时任务(默认15分钟)

    /**
     * Schedule the task that updates <em>renewal threshold</em> periodically.
     * The renewal threshold would be used to determine if the renewals drop
     * dramatically because of network partition and to protect expiring too
     * many instances at a time.
     *
     */
    private void scheduleRenewalThresholdUpdateTask() {
        timer.schedule(new TimerTask() {
                           @Override
                           public void run() {
                               updateRenewalThreshold();
                           }
                       }, serverConfig.getRenewalThresholdUpdateIntervalMs(),
                serverConfig.getRenewalThresholdUpdateIntervalMs());
    }

自我保护阀值的计算公式

    protected void updateRenewsPerMinThreshold() {
        this.numberOfRenewsPerMinThreshold = (int) (this.expectedNumberOfClientsSendingRenews
                * (60.0 / serverConfig.getExpectedClientRenewalIntervalSeconds())
                * serverConfig.getRenewalPercentThreshold());
    }

自我保护阀值 = 服务总数 * 每分钟续约数 * 自我保护续约百分比阀值因子。

每分钟续约数 =(60S/客户端续约间隔)

最后自我保护阀值的计算公式为:

自我保护阀值 = 服务总数 * (60S/客户端续约间隔) * 自我保护续约百分比阀值因子。

备注:在老版本中阀值的计算有点错误,新版本中修复成现在的计算公式。

自我保护逻辑

Eureka Server提供了一个EvictionTask定时清理续约超时的服务。清理之前首先判断是否需要清理(isLeaseExpirationEnabled())。这个判断方法首先会判断是否开启了自我保护开关,如果开启了,则会继续判断上一分钟的续约数是否小于自我保护阀值,如果上一分钟的续约数(numOfRenewsInLastMin)小于自我保护阀值(numberOfRenewsPerMinThreshold),则开启自我保护机制,不再进行服务的剔除。

    public boolean isLeaseExpirationEnabled() {
        if (!isSelfPreservationModeEnabled()) {
            // The self preservation mode is disabled, hence allowing the instances to expire.
            return true;
        }
        return numberOfRenewsPerMinThreshold > 0 && getNumOfRenewsInLastMin() > numberOfRenewsPerMinThreshold;
    }

备注1:numberOfRenewsPerMinThreshold是对所有服务总阀值的计算,而不是单个服务的计算。

备注2: Eureka默认的自我保护阀值因子是85%,可以通过renewalPercentThreshold修改。

 

Spring Cloud实战项目Jbone地址

github地址:https://github.com/417511458/jbone

码云地址:https://gitee.com/majunwei2017/jbone

马军伟
关于作者 马军伟
写的不错,支持一下

先给自己定个小目标,日更一新。