Spring Cloud Eureka的自我保护模式与实例下线剔除

之前我说明了Eureka注册中心的保护模式,由于在该模式下不能剔除失效节点,故按原有配置在实际中不剔除总感觉不是太好,所以深入研究了一下。当然,这里重申一下,不管实例是否有效剔除,消费端实现Ribbon重试机制也是必须的。

说下背景,在微服务架构中,有个CAP原则(一致性,可用性,可靠性),三者由于存在互斥,只能同时满足其二,第三点需要有一定舍弃。Eureka舍弃了强一致性,所以在进入保护模式后,失效节点的一致性不能得到保证。

以下是我验证后的几种方式,可以实现服务的及时剔除。

1、关闭自我保护模式eureka.server.enable-self-preservation=false来关闭。但是,这种方式有违eureka的CAP原则,所以,我并不推荐这种方式。

2、在说明之前,我们先看下源码。

以下代码判断是否进入刷新服务列表的步骤。该代码在AbstractInstanceRegistry.java和PeerAwareInstanceRegistryImpl.java中。

public void evict(long additionalLeaseMs) {
        logger.debug("Running the evict task");
        //主要是看isLeaseExpirationEnabled返回值
        if (!isLeaseExpirationEnabled()) {
            logger.debug("DS: lease expiration is currently disabled.");
            return;
        }
        //进入筛选过期实例的方法。这里省略   
}   
public boolean isLeaseExpirationEnabled() {
        //isSelfPreservationModeEnabled由是否开启保护模式配置决定,默认为true
        if (!isSelfPreservationModeEnabled()) {
            return true;
        }
        return numberOfRenewsPerMinThreshold > 0 && getNumOfRenewsInLastMin() > numberOfRenewsPerMinThreshold;
    }

主要看numberOfRenewsPerMinThreshold和getNumOfRenewsInLastMin()方法。我们主要看numberOfRenewsPerMinThreshold初始化的地方。getNumOfRenewsInLastMin()方法是统计最后一分钟内的心跳统计总数。

//PeerAwareInstanceRegistryImpl.java
 public void openForTraffic(ApplicationInfoManager applicationInfoManager, int count) {
        // Renewals happen every 30 seconds and for a minute it should be a factor of 2.
        this.expectedNumberOfRenewsPerMin = count * 2;
        this.numberOfRenewsPerMinThreshold =
                (int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold());
//....
}

count是注册在注册中心的实例总数,包括高可用的另外注册中心。我们可以看到,这里是以硬编码的方式初始化expectedNumberOfRenewsPerMin,通过 实例数*2。为什么*2,因为实例默认发送心跳时间是30s,所以通过*2统计一分钟类应收到的总的心跳次数,因为是硬编码,所以不建议修改实例端心跳周期时间!。numberOfRenewsPerMinThreshold是通过总的心跳数乘以允许失败的比例。默认为0.85。expectedNumberOfRenewsPerMin在默认情况下,会每隔15分钟刷新一次。

通过上面分析,我们大致了解了不通过关闭自我保护模式下触发服务剔除操作的条件。得出以下两种方案来实现失效节点的剔除(亲测有效)。

  1. 通过修改每分钟心跳成功最低比例来控制注册中心不进入自我保护模式,实时剔除节点(需要等到实例定义的过期时间,且注册中心触发刷新周期)。eureka.server.renewal-percent-threshold来配置比例。举个例子,当前我们有4个服务实例,计算所知,每分钟有4*2=8个心跳,当需要实现一个实例意外掉线后,则每分钟实际收到心跳为6个,8*x<6时不会进入保护模式,则x<0.75,配置为0.75以下大致可以实现剔除操作,但是由于网络不稳定因素,存在心跳异常,所以该值尽量设置小于0.75多点,如0.5。为什么默认会0.85呢,我觉得应该是有算法支撑,但从结果来看,保护模式还是适用于大规模的服务集群,当一台或几台挂掉之后,也不会进入保护模式,默认比例下,可以实现实例的剔除。如果集群较小,如2台,挂掉一台就会进入保护模式,且保护模式意义不大,可以关闭自我保护模式。
  2. 通过设置实例的心跳时间,改为较小的周期。由于需要判断每分钟实际心跳数>实例总数*2*0.85,才不会进入自我保护模式,所以设置心跳周期较小,使实际心跳数多于比例下的心跳数,可以实现实例的及时剔除。实例的心跳周期设置:eureka.instance.lease-renewal-interval-in-seconds,如设置为5等等,具体可根据以上算法来计算合适的值。

猜你喜欢

转载自www.cnblogs.com/ibethfy/p/9593732.html