注册中心 Eureka 源码解析 —— 应用实例注册发现 (四)之自我保护机制

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

转载声明:转载请注明出处,本技术博客是本人原创文章

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> 注册中心 Eureka 源码解析 —— 应用实例注册发现 (四)之自我保护机制

本文主要基于 Eureka 1.8.X 版本

    1. 概述
    1. 定义
    1. 实现
  • 3.1 触发条件
  • 3.2 计算公式
  • 3.3 计算时机
    1. 彩蛋

1. 概述

本文主要分享 自我保护机制,为应用实例过期下线做铺垫。

推荐 Spring Cloud 书籍

  • 请支持正版。下载盗版,等于主动编写低级 BUG 。
  • 程序猿DD —— 《Spring Cloud微服务实战》
  • 周立 —— 《Spring Cloud与Docker微服务架构实战》

推荐 Spring Cloud 视频

  • Java 微服务实践 - Spring Boot
  • Java 微服务实践 - Spring Cloud
  • Java 微服务实践 - Spring Boot / Spring Cloud

2. 定义

自我保护机制定义如下:

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

为什么使用自动保护机制 ?你也可以从周立兄的这篇文章得到答案,这里笔者就不一本正经的胡说八道了。

3. 实现

首先,我们来看下在自动保护机制里扮演重要角色的两个变量:


// AbstractInstanceRegistry.java
/**
* 期望最小每分钟续租次数
*/
protected volatile int numberOfRenewsPerMinThreshold;
/**
* 期望最大每分钟续租次数
*/
protected volatile int expectedNumberOfRenewsPerMin;
  • expectedNumberOfRenewsPerMin ,期望最大每分钟续租次数。
  • numberOfRenewsPerMinThreshold ,期望最小每分钟续租次数。

3.1 触发条件

当每分钟心跳次数(  renewsLastMin ) 小于  numberOfRenewsPerMinThreshold 时,并且开启自动保护模式开关(  eureka.enableSelfPreservation = true ) 时,触发自动保护机制,不再自动过期租约,实现代码如下:


// AbstractInstanceRegistry.java
public void evict(long additionalLeaseMs) {

   if (!isLeaseExpirationEnabled()) {
       logger.debug("DS: lease expiration is currently disabled.");
       return;
   }

   // ... 省略过期租约逻辑
}

// PeerAwareInstanceRegistryImpl.java
@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;
}

3.2 计算公式

计算公式如下:

  • expectedNumberOfRenewsPerMin = 当前注册的应用实例数  x 2
  • numberOfRenewsPerMinThreshold =  expectedNumberOfRenewsPerMin  * 续租百分比(  eureka.renewalPercentThreshold )

为什么乘以 2

默认情况下,注册的应用实例每半分钟续租一次,那么一分钟心跳两次,因此 x 2 。

这块会有一些硬编码的情况,因此不太建议修改应用实例的续租频率

为什么乘以续租百分比

低于这个百分比,意味着开启自我保护机制。

默认情况下, eureka.renewalPercentThreshold = 0.85 。

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

3.3 计算时机

目前有个地方会计算  numberOfRenewsPerMinThreshold 、  expectedNumberOfRenewsPerMin,我们逐小节来看。

3.3.1 Eureka-Server 初始化

Eureka-Server 在启动时,从 Eureka-Server 集群获取注册信息,并首次初始化  numberOfRenewsPerMinThreshold 、  expectedNumberOfRenewsPerMin 。实现代码如下:


// 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());

   // ... 省略其它代码
}   

3.3.2 定时重置

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


// 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);
   }
}
  • 配置  eureka.renewalThresholdUpdateIntervalMs 参数,定时重新计算。默认,15 分钟。
  • 代码块  !this.isSelfPreservationModeEnabled() :当未开启自我保护机制时,每次都进行重新计算。事实上,这两个参数不仅仅自我保护机制会使用到,配合 Netflix Servo 实现监控信息采集  numberOfRenewsPerMinThreshold expectedNumberOfRenewsPerMin
  • 代码块  (count * 2) (serverConfig.getRenewalPercentThreshold() * numberOfRenewsPerMinThreshold) :当开启自我保护机制时,应用实例每分钟最大心跳数(  count * 2 ) 小于期望最小每分钟续租次数(  serverConfig.getRenewalPercentThreshold() * numberOfRenewsPerMinThreshold ),不重新计算。如果重新计算,自动保护机制会每次定时执行后失效

3.3.3 应用实例注册

应用实例注册时,增加  numberOfRenewsPerMinThreshold 、 expectedNumberOfRenewsPerMin 。实现代码如下:


// 
public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {

    // ... 省略无关代码

    // The lease does not exist and hence it is a new registration
    // 【自我保护机制】增加 `numberOfRenewsPerMinThreshold` 、`expectedNumberOfRenewsPerMin`
    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());
         }
     }

     // ... 省略无关代码
}

3.3.4 应用实例下线

应用实例下线时,减少  numberOfRenewsPerMinThreshold 、 expectedNumberOfRenewsPerMin 。实现代码如下:


// PeerAwareInstanceRegistryImpl.java
@Override
public boolean cancel(final String appName, final String id,
                     final 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());
        }
   }

   // ... 省略无关代码
}

666. 彩蛋知识星球

😈 终于完整理解 Eureka-Server 自我保护机制,满足。噶~~

推荐另一篇 Eureka-Server 自我保护机制源码分析文章:《理解eureka的自我保护机制》 。

胖友,分享我的公众号( 芋道源码 ) 给你的胖友可好?

注册中心 Eureka 源码解析 —— 应用实例注册发现 (四)之自我保护机制
本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

转载声明:转载请注明出处,本技术博客是本人原创文章

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> 注册中心 Eureka 源码解析 —— 应用实例注册发现 (四)之自我保护机制


 上一篇
注册中心 Eureka 源码解析 —— 应用实例注册发现(六)之全量获取 注册中心 Eureka 源码解析 —— 应用实例注册发现(六)之全量获取
中文详细注释的开源项目 Java 并发源码合集 RocketMQ 源码合集 Sharding-JDBC 源码解析合集 Spring MVC 和 Security 源码合集 MyCAT 源码解析合集 摘要: 原创出处 http://www
2021-04-05
下一篇 
注册中心 Eureka 源码解析 —— 应用实例注册发现 (三)之下线 注册中心 Eureka 源码解析 —— 应用实例注册发现 (三)之下线
中文详细注释的开源项目 Java 并发源码合集 RocketMQ 源码合集 Sharding-JDBC 源码解析合集 Spring MVC 和 Security 源码合集 MyCAT 源码解析合集 本文主要基于 Eureka 1.8.X
2021-04-05