神秘的 Eureka 自我保护
本文翻译自The Mystery of Eureka Self-Preservation
根据CAP定理, Eureka是一个AP系统, 这就导致了在网络分区期间多个注册表中的信息不一致. 自我保护功能则是为了尽可能降低这种不一致.
自我保护的定义
自我保护(self preservation)是Eureka的一项功能, Eureka注册表在未收到实例的心跳情况超过一定阈值时停止驱逐过期的实例.
从一个健康的系统开始
把下面看成一个健康的系统
假设所有的微服务都处于健康的状态并成功注册到Eureka注册表中.
多个注册表间会同步注册表记录, 所有的微服务实例都处于UP状态. 假设实例2从注册中心发现里实例4, 并调用实例4上的服务.
突发网络分区
假设出现了网络分区, 系统变成下面的状态.
由于网络分区, 实例4和5丢失了注册中心的连接, 但是实例2仍然可以连接到实例4. Eureka服务端因为没有收到实例4和5的心跳(超过一定时间后), 将他们驱逐. 然后Eureka服务端意识到突然丢失了超过15%(2/5)的心跳, 因此其进入自我保护模式
从此时开始, Eureka服务端不在驱逐任何实例, 即使实例真正的下线了.
实例3下线, 但其始终存在注册表中.
但此时注册表还会接受新实例的注册.
自我保护的基本原理
自我保护功能在下面两种情况下是合理的:
- Eureka服务端因为弱网分区问题没有收到心跳(这并不意味着客户端下线), 但是这种问题可能会很快被修复.
- 即使Eureka服务端和客户端的连接断开, 客户端间还可以继续保持连接. (比如上面实例2仍然可以连接到实例4)
配置 (默认)
下面的配置会直接或间接影响到自我保护的行为.
eureka.instance.lease-renewal-interval-in-seconds = 30
客户端发送心跳的频率. 服务端会以此在计算期望收到心跳数, 默认30秒
eureka.instance.lease-expiration-duration-in-seconds = 90
多长时间未收到心跳后, 实例才可以被驱逐, 默认90秒
eureka.server.eviction-interval-timer-in-ms = 60 * 1000
Eureka服务端驱逐操作的执行频率, 默认60秒
eureka.server.renewal-percent-threshold = 0.85
期望心跳数达到该阈值后, 就会进入自我保护模式, 默认0.85
eureka.server.renewal-threshold-update-interval-ms = 15 * 60 * 1000
期望心跳数的计算间隔, 默认15分钟
eureka.server.enable-self-preservation = true
是否允许Eureka服务端进入自我保护模式, 默认开启
理解配置
Eureka服务端在"上一分钟实际收到的心跳数"小于"每分钟期望的心跳数"时就会进入自我保护模式
期望的每分钟心跳数
假设renewal-percent-threshold
设置为0.85
计算方式:
- 单个实例每分钟期望的心跳数是: 21
- N个实例的每分钟期望的心跳数: 2 * N
- 期望的上一分钟最小心跳数: 2 * N * 0.85
实际的每分钟心跳数
正如上面所述, 两个定时调度器独立地运行计算实际和期望的心跳数. 此外还有另一个调度任务EvictionTask
进行结果比较, 并识别当前系统是否在自我保护状态.
这个调度任务每个eviction-interval-timer-in-ms
时间执行一次, 并决定是否驱逐实例.
结论
- 基于使用的经验, 大多数情况下自我保护模式都是错误的, 它错误地认为一些下线的微服务实例是不良的网络分区
- 自我保护永远不会过期, 除非下线的实例重新上线
- 如果启用了自我保留, 则无法对实例的心跳间隔进行微调, 因为自我保护在计算期望心跳数是按照30s间隔来计算的
- 除非环境中经常出现类似的网络分区故障, 否则建议关闭
-
这个值是固定的, 源于默认的心跳间隔是30s, 故每分钟2次. 见eureka-core-1.7.2的
AbstractInstanceRegistryL226
↩︎