• Eureka的自我保护


    Eureka的自我保护

    当Eureka Server节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server节点会自动退出自我保护模式。

    如果想研究自我保护机制,那就要从服务下线那里,因为自我保护来决定是让eureka进行剔除服务的,下线代码如下:

     public void evict(long additionalLeaseMs) {
            logger.debug("Running the evict task");
    
            //是否允许主动摘除故障的服务实例
            if (!isLeaseExpirationEnabled()) {
                logger.debug("DS: lease expiration is currently disabled.");
                return;
            }
    
           ....
     }           
    
        @Override
        public boolean isLeaseExpirationEnabled() {
            //默认是true,关闭自我保护机制的话,这个方法永远返回true,随时都可以清除故障的服务实例。
            if (!isSelfPreservationModeEnabled()) {
                // The self preservation mode is disabled, hence allowing the instances to expire.
                return true;
            }
            //numberOfRenewsPerMinThreshold  这个是我期望的一分钟发送多少心跳。
            //getNumOfRenewsInLastMin 这个是上一分钟一共发来多少心跳
            return numberOfRenewsPerMinThreshold > 0 && getNumOfRenewsInLastMin() > numberOfRenewsPerMinThreshold;
        }
    

    触发的机制就是说,如果最近一分钟发过来的心跳小于期望的值,这进入心跳保护机制。那么就来看一下,这个期望的值是什么算出来的。

    首先在初始化的时候,就会给他先赋值,默认值为其他server拉着的注册表信息的数量乘以2

    // EurekaBootStrap.java
    protected void initEurekaServerContext() throws Exception {
    
        // ... 省略其它代码
    
        // 【2.2.10】从其他 Eureka-Server 拉取注册信息
        // Copy registry from neighboring eureka node
        int registryCount = registry.syncUp();
        registry.openForTraffic(applicationInfoManager, registryCount);
        
        // ... 省略其它代码
    }
    
    // PeerAwareInstanceRegistryImpl.java
    @Override
    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());
               
    
    

    然后再上线,下线,故障的时候都会对他进行修改。

    image-20211011213620374

    由于以前的版本为硬编码,是直接乘以2的,近期的版本,对代码进行了优化,因为心跳是30秒发来一次,所以直接用60/30,然后在乘以0.85。

    为什么乘以续租百分比

    低于这个百分比,意味着开启自我保护机制。默情况下,eureka.renewalPercentThreshold = 0.85

    如果你真的调整了续租频率,可以等比去续租百分比,以保证合适的触发自我保护机制的阀值。另外,你需要注意,续租频率是 Client 级别,续租百分比是 Server 级别。

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

    以前的代码是有bug的,只对下线的代码进行了修改,故障是没有的,近期版本也做了修复。把剔除服务的相关代码都抽取了出来了,形成了internalCancel方法,被下面的2个方法调用,在它的方法里,做了减法。

    image-20211011213851595

    image-20211011214105920

    定时更新

    Eureka-Server 定时重新计算 numberOfRenewsPerMinThresholdexpectedNumberOfRenewsPerMin 。实现代码如下:

    // PeerAwareInstanceRegistryImpl.java
    private void scheduleRenewalThresholdUpdateTask() {
       timer.schedule(new TimerTask() {
                          @Override
                          public void run() {
                              updateRenewalThreshold();
                          }
                      }, serverConfig.getRenewalThresholdUpdateIntervalMs(),
               serverConfig.getRenewalThresholdUpdateIntervalMs());
    }
    
    // AbstractInstanceRegistry.java
    /**
    * 自我保护机锁
    *
    * 当计算如下参数时使用:
    *  1. {@link #numberOfRenewsPerMinThreshold}
    *  2. {@link #expectedNumberOfRenewsPerMin}
    */
    protected final Object lock = new Object();
    
    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;
                   }
               }
           }
           // 计算 expectedNumberOfRenewsPerMin 、 numberOfRenewsPerMinThreshold 参数
           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);
       }
    }
    
    • Registry注册表,默认是15分钟,会跑一次定时任务,算一下服务实例的数量,如果从别的eureka server拉取到的服务实例的数量,大于当前的服务实例的数量,会重新计算一下,主要是跟其他的eureka server做一下同步
  • 相关阅读:
    Java并发初识
    go交叉编译
    MRC与ARC混合开发配置
    Hibernate配置文件
    LEFT JOIN重复数据
    Ext.ViewPort布局
    Hibernate学习映射文件
    AjaxMethod方法
    DataBinder
    subsonic 获取记录数量
  • 原文地址:https://www.cnblogs.com/dalianpai/p/15395168.html
Copyright © 2020-2023  润新知