Netflix Eureka - Eureka Server自我保护机制

116 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。


Netflix Eureka 目录汇总

  1. eureka server启动以及初始化

  2. eureka client启动以及初始化

  3. 服务注册

    3.1 可重入读写锁-读锁

  4. 服务发现

    4.1. 全量抓取注册表

    4.2. 注册表多级缓存机制

    4.3. 注册表多级缓存过期机制(主动、定时、被动)

    4.4. 增量抓取注册表

     4.4.1. 一致性Hash对比机制
    
     4.4.2. 可重入读写锁-写锁
    
  5. 服务续约

  6. 服务下线

  7. 服务故障自动感知及服务实例自动摘除

  8. 自我保护机制


Netflix Eureka 时间间隔简要

服务注册-

服务发现-

读写缓存-定时过期(180秒)

只读缓存-被动过期(30秒(整30秒))

定时抓取增量注册表(30秒)

定时删除超过3分钟的服务实例变更记录(30秒)

服务续约(30秒)

服务下线

服务故障自动感知及服务实例自动摘除(感知:90秒(BUG:90 * 2秒);摘除:60秒)

自我保护机制

每分钟期望的续约次数 & 每分钟期望的续约次数阈值(15分钟)

MeasuredRate renewsLastMin(60秒)

自我保护机制

假如说,20个服务实例,结果在1分钟之内,只有8个服务实例保持了心跳 --> eureka server是应该将剩余的12个没有心跳的服务实例都摘除吗?

这个时候很可能说的是,eureka server自己网络故障了,那些服务没问题的。只不过eureka server自己的机器所在的网络故障了,导致那些服务的心跳发送不过来。就导致eureka server本地一直没有更新心跳。

针对这种情况eureka server自己进入一个自我保护的机制,从此之后就不会再摘除任何服务实例了

注册表的evict()方法,EvictionTask,定时调度的任务,60s来一次,会判断一下服务实例是否故障了,如果故障了,一直没有心跳,就会将服务实例给摘除。

  1. evict()方法内部,先会判断上一分钟的心跳次数,是否小于期望的一分钟的心跳次数,如果小于,那么压根儿就不让清理任何服务实例

  2. 期望的一分钟的心跳次数是怎么算出来的?

(1)eureka server启动的时候初始化

registry.openForTraffic(applicationInfoManager, registryCount);

完成了numberOfRenewsPerMinThreshold这个值,期望一分钟得有多少次心跳的值,初始化。刚开始会调用syncUp()的方法,从相邻的eureka server节点,拷贝过来注册表,如果是自己本地还没注册的服务实例,就在自己本地注册一下。

会记录一下从别的eureka server拉取过来的服务实例的数量,将这个服务实例的数量,就作为自己eureka server本地初始化的这么一个服务实例的数量。将服务实例数量 * 2。

期望心跳次数的计算硬编码BUG,后续版本通过updateRenewsPerMinThreshold()方法已修复。

用这个服务实例 * 2 * 0.85 = 20 * 2 * 0.85 = 34,期望的是最少一分钟20个服务实例,得有34个心跳。根据当前的服务实例的数量,计算出来的一分钟最少需要的心跳次数。服务实例个数 * (60 / 心跳时间间隔) * 0.85 = 每分钟最少的心跳次数。

(2)注册、下线、故障

每分钟期望的心跳的次数,是跟服务实例的数量相关的,服务实例随着上线和下线(故障情况在之后版本修复的),都在不断的变动着。注册的时候,每分钟期望心跳次数 + 2。服务下线的时候,直接每分钟期望心跳次数 - 2。

故障的时候,摘除一个服务实例,没有直接更新期望心跳次数。使用15分钟一次的定时任务updateRenewalThreshold()更新expectedNumberOfRenewsPerMinnumberOfRenewsPerMinThreshold

实际的心跳次数比期望的心跳次数要小,会假定是eureka server网络问题,而不是eureka client服务实例的问题,不会再摘除任何服务实例。

(3)定时更新

Registry注册表,默认是15分钟,会跑一次定时任务,算一下服务实例的数量,如果本地缓存的applications服务实例的数量,大于当前的服务实例的期望的数量时,会重新计算一下。(只有当阈值大于当前预期阈值 或者 自我保护未开启,才更新阈值。)

  1. 实际的上一分钟的心跳次数是怎么算出来的

每次一个心跳过来,调用MeasuredRate.increment()方法增加心跳记录次数。

内部的三个属性:

lastBucket:用来保存上一个完整周期统计的心跳记录次数;

currentBucket:用来记录当前未完成周期的心痛记录统计次数;

timer定时器:用来每隔60秒将currentBucket属性统计的心跳记录统计次数复制给lastBucket,并将currentBucket赋值为0,开始新的每分钟心跳记录次数统计周期。

  1. 自我保护机制的触发

如果上一分钟实际的心跳次数,比我们期望的一分钟的心跳次数要小,触发自我保护机制,不允许摘除任何服务实例,此时认为自己的eureka server出现网络故障,大量的服务实例无法发送心跳过来

  1. 关于15分钟内心跳次数小于85%阈值才开启自我保护机制的争议。

服务注册、服务下线会直接触发【续约阈值】的更新,故障感知剔除不会直接出发【续约阈值】的更新,需要配合15分钟一次的【续约阈值更新】任务的调度才会更新【续约阈值】