09、Eureka源码系列-自我保护

正文

自我保护机制是指,eureka-sever如果在1min内,发现超过15%的大量服务实例下线了,会认为是自身出了问题,比如断网,导致服务实例client无法向自己发送心跳,这时不会将这些服务实例剔除。

自我保护机制的入口:

  • com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#isLeaseExpirationEnabled

 

方法实现比较简单:

1、 如果isLeaseExpirationEnabled方法返回true,则执行服务剔除的逻辑,否则直接返回;
2、 如果isSelfPreservationModeEnabled返回false,说明没有打开自我保护机制,则方法直接返回true,执行服务剔除的逻辑;
3、 默认情况下自我保护机制是打开的,则判断是否getNumOfRenewsInLastMin()>numberOfRenewsPerMinThreshold

逻辑比较简单,但是引入了两个变量:

  • numberOfRenewsPerMinThreshold 每分钟期望收到的心跳数阈值
  • getNumOfRenewsInLastMin 上一分钟实际收到的心跳数

每分钟期望收到的心跳数阈值

每分钟期望收到的心跳数阈值 = 每分钟期望心跳数 * renewalPercentThreshold

renewalPercentThreshold默认是0.85,也就是说,每分钟期望收到的心跳数阈值是每分钟期望心跳数的0.85倍。

每分钟期望心跳数在server启动的时候会从集群内其他节点拷贝过来,服务实例有上下线更新的时候会更新心跳数。

每15min还会执行每分钟期望收到的心跳数阈值的定时任务:

com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#init

重新根据注册表计算一次准确的每分钟期望收到的心跳数阈值

上一分钟实际收到的心跳数

在讲《服务踢除》的时候我们提到过com.netflix.eureka.registry.AbstractInstanceRegistry#postInit方法,当时忽略了一些代码,现在我们重新看一下这个方法:

 

这个方法第一行启动了一个定时任务,用来每60s统计上一分钟内收到的心跳数。(eureka的源码中有的关键机制代码写的太隐蔽了,读起来很费劲,我认为好的开源项目的代码应该是很易读的😕。

 

这个方法维护两个变量:

  • 当前一分钟的心跳
  • 上一分钟的心跳

通过每1min将当前一分钟的心跳转移到清零并转移到上一分钟的心跳中,就能统计出每分钟实际收到的心跳数。代码实现不复杂,但是这种通过定时任务来做时间段统计的思路,笔者觉得还是挺好玩的。

增加心跳

 

从方法调用点可以看出,当有服务实例来进行心跳续约的时候,当前一分钟心跳数就会+1。

总结

通过维护两个变量:

  • 每分钟期望收到的心跳数阈值
  • 上一分钟实际收到的心跳数

实现了在1min内有超过15%的服务实例下线时,eureka-server会是自己的网络有问题导致无法接收心跳,从而进入自我保护机制,防止将大量服务实例下线

配置参数

是否启动server自动保护机制

enableSelfPreservation = true