spring cloud:eureka源码之我见(九)自我保护机制

假如说,20个服务实例,结果在1分钟之内,只有8个服务实例保持了心跳 ,这时eureka server是应该将剩余的12个没有心跳的服务实例都摘除吗?

eureka server这时会考虑是否自己网络故障了,而服务是没问题的。只不过eureka server自己的机器所在的网络故障了,导致那些服务的心跳发送不过来。就导致eureka server本地一直没有更新心跳。

这里eureka server会进入一个自我保护机制,停止摘除服务实例。

上文我们看到注册表registry的evict()方法,EvictionTask,定时调度的任务,60s来一次,会判断一下服务实例是否故障了,如果故障了,一直没有心跳,就会将服务实例给摘除。

但是在判断服务实例是否故障之前,evict()还会对当前服务器的状态进行判断

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

当isLeaseExpirationEnabled返回false的时候,eureka server启动自我保护机制,停止摘除任何服务实例。

那么isLeaseExpirationEnabled中是如何判断服务端出现问题,启动自我保护机制的呢?

自我保护机制完整流程:

(1)eureka server初始化的时候,就会初始化一个值numberOfRenewsPerMinThreshold(一分钟期望心跳次数阈值)

int registryCount = registry.syncUp();

在eureka server的启动类EurekaBootStrap中,首先通过syncUp()方法从相邻的eureka server中拉取注册表。如果发现eurekaClient中服务实例数量为0,则等待30秒后再查看一次(根据配置参数这样最多重复五次,这里并不会重复去拉取注册表,而只是等待,然后查看。拉取注册表的行为在其他流程中)。

如果发现拉取到的服务实例在本地还没注册,则在本地进行注册。

registry.openForTraffic(applicationInfoManager, registryCount);

这个方法就会根据当前服务实例的数量完成numberOfRenewsPerMinThreshold的初始化

        this.expectedNumberOfRenewsPerMin = count * 2;
        this.numberOfRenewsPerMinThreshold =
                (int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold());

期望心跳次数 = 服务实例 * 2

一分钟期望心跳次数阈值 = 期望心跳次数 * serverConfig.getRenewalPercentThreshold()(默认为0.85)

(2)每分钟期望的心跳次数与服务实例是相关的,如果服务实例上线,则期望心跳次数会+2,如果服务实例下线,期望心跳次数会-2。理论上来说,当服务实例故障下线时,期望心跳次数也会 -2,但是源码中并没有这个体现,但是这里摘除服务实例时,并没有去改变期望心跳次数。

(3)启动类EurekaBootStrap中,有一行代码

serverContext.initialize();

在这里,会启动一个定时调度任务

    private void scheduleRenewalThresholdUpdateTask() {
        timer.schedule(new TimerTask() {
                           @Override
                           public void run() {
                               updateRenewalThreshold();
                           }
                       }, serverConfig.getRenewalThresholdUpdateIntervalMs(),
                serverConfig.getRenewalThresholdUpdateIntervalMs());
    }

延迟15分钟(默认)启动,然后每隔15分钟(默认)运行一次

private void updateRenewalThreshold() {
        try {
            Applications apps = eurekaClient.getApplications();
            int count = 0;
            for (Application app : apps.getRegisteredApplications()) {
                for (InstanceInfo instance : app.getInstances()) {
                    if (this.isRegisterable(instance)) {
                        ++count;
                    }
                }
            }
            synchronized (lock) {
                // Update threshold only if the threshold is greater than the
                // current expected threshold of if the self preservation is disabled.
                if ((count * 2) > (serverConfig.getRenewalPercentThreshold() * numberOfRenewsPerMinThreshold)
                        || (!this.isSelfPreservationModeEnabled())) {
                    this.expectedNumberOfRenewsPerMin = count * 2;
                    this.numberOfRenewsPerMinThreshold = (int) ((count * 2) * serverConfig.getRenewalPercentThreshold());
                }
            }
            logger.info("Current renewal threshold is : {}", numberOfRenewsPerMinThreshold);
        } catch (Throwable e) {
            logger.error("Cannot update renewal threshold", e);
        }
    }

从(2)中我们可以看到如果服务实例故障被摘除,期望心跳次数的阈值并不会改变。如果不去定时的改变期望心跳次数的阈值,那么随着服务实例故障被摘除的数量变多,心跳次数就会低于期望心跳次数的阈值,导致自我保护机制触发,之后就不能摘除心跳超时的服务实例了。

当然更新期望心跳次数阈值也是有条件的,如果server当前处于自我保护状态下,就不能随意改变,因为这样会导致server退出自我保护机制。所以更新总值和临界值的前提是当前Server不处于自我保护状态,也就是上一分钟的续约总数的比例超过85%。

(4)实际心跳次数的计算

在renew方法中,每次有心跳过来,都会去更新MeasuredRate,将其内部的currentBucket使用CAS + 1。在MeasuredRate内部也有一个定时调度任务,每隔一分钟,就会将currentBucket的值存储到lastBucket中,并将currentBucket置0。

lastBucket就是用于判断的实际心跳次数。

(5)自我保护机制的触发

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

当代码中配置了启动自我保护机制,或者当前心跳次数小于每分钟期望心跳次数的阈值,则触发自我保护机制,eureka server认为自己网络故障,不再摘除任何服务实例。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值