Eureka的自我保护机制

背景

Eureka的自我保护机制是为了防止误杀服务。当注册中心发生故障,服务不能够正常的续约,但是服务运行正常,默认情况下,Eureka会将超过90s未续约的服务进行移除。这样做明显不合理,所以Eureka提供了一个自我保护机制。

Eureka服务端与客户端如何沟通?

1. 服务注册

进行服务注册时,Eureka Client会向Eureka Server发送第一次心跳,会将服务的实例信息注册到注册中心

2. 服务注册

Eureka Client 每30秒来发送一次心跳来更新实例信息。通知Eureka Server该实例仍然存在。如果超过90秒没有发送更新,则服务器将从注册信息中将此服务移除。

续约时间可以更改,建议不要更改。

3. 获取注册表

Eureka ClientEureka Server获取注册表信息并在本地缓存它,之后,客户端使用该信息来查找其他服务。此信息会定期更新,30s更新一次,每次发送心跳的时候,就会与Eureka Server进行信息同步。

4. 取消请求

Eureka Client在关闭时,会发送一个shutdown请求。这将从Eureka Server的实例注册表中删除实例,从而有效地使实例脱离流量。

5.定时刷新

Eureka Server会定时来刷新缓存,来确保注册服务的可用性。Eureka Client会定期来拉取(Fetch)注册信息。


故障判断

这里存在一个问题,如何判断是Eureka Server故障,还是服务故障,Eureka Server提供的判断条件是,当出现大量的服务续约超时,那么就会认为自己出现了问题。如果出现少量了服务续约超时,则认为服务故障。

开启时机

是否开启自我保护机制,实质上就是计算续约的服务,由于续约服务的计算是间歇性的,因此判断的条件就是两次更新的间隔的差值,这里提供了一个阀值numberOfRenewsPerMinThreshold和上一分钟的续约数进行对比,如果实际的续约数小于了自我保护阀值,则开启自我保护。
自我保护阀值的计算:
com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl

  1. 定时更新
private void scheduleRenewalThresholdUpdateTask() {
        timer.schedule(new TimerTask() {
                           @Override
                           public void run() {
                               updateRenewalThreshold();
                           }
                       }, serverConfig.getRenewalThresholdUpdateIntervalMs(),
                serverConfig.getRenewalThresholdUpdateIntervalMs());
 }
 
 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);
        }
    }
  1. 服务注册
    com.netflix.eureka.registry.AbstractInstanceRegistrypublic void register(InstanceInfo registrant, int leaseDuration, boolean isReplication)方法
   synchronized (lock) {
                    if (this.expectedNumberOfRenewsPerMin > 0) {
                        // Since the client wants to cancel it, reduce the threshold
                        // (1
                        // for 30 seconds, 2 for a minute)
                        this.expectedNumberOfRenewsPerMin = this.expectedNumberOfRenewsPerMin + 2;
                        this.numberOfRenewsPerMinThreshold =
                                (int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold());
                    }
                }
                logger.debug("No previous lease information found; it is new registration");
  1. 服务取消
    com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl
  @Override
    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.expectedNumberOfRenewsPerMin > 0) {
                    // Since the client wants to cancel it, reduce the threshold (1 for 30 seconds, 2 for a minute)
                    this.expectedNumberOfRenewsPerMin = this.expectedNumberOfRenewsPerMin - 2;
                    this.numberOfRenewsPerMinThreshold =
                            (int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold());
                }
            }
            return true;
        }
        return false;
    }

numberOfRenewsPerMinThreshold会取在一次间隔时间(默认一分钟)内包括定时刷新服务取消注册服务三方面对总和。

开启保护判断条件

如果自我保护关闭,直接返回。否则如果上一分钟的续约数大于阀值则开启自我保护。开启自我保护后,所有进行注册的服务将不会被移除。

  @Override
    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的默认阀值因子是85%,可以通过eureka.renewalPercentThreshold=[0.0, 1.0].进行修改。

提示信息出现的时机

验证完自我保护机制开启后,并不会马上呈现到web上,而是默认需等待 5 分钟(可以通过eureka.server.wait-time-in-ms-when-sync-empty配置),即 5 分钟后你会看到下面的提示信息:
在这里插入图片描述

如果关闭自我保护

通过设置eureka.enableSelfPreservation=false来关闭自我保护功能。

注册中心如何恢复正常
  1. 它看到的心跳续订数量已经超过预期的阈值
  2. 自我保护关闭

自我保护机制不是一种错误,而是一种Eureka的保护机制。它是有意义的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值