原创不易,转载请注明出处


1.自我保护机制介绍

在这里插入图片描述

想必用过eureka 的同学都在eureka web控制台见过这么一行大红字,我现在还能想起来第一次用eureka 出现这个的心里的那种感觉,什么东西,这么显眼,然后就很懵逼,立马搜了一下这个是什么鬼,就搜到说eureka自我保护机制触发了,说是 “eureka 在运行期间会去统计心跳失败比例在 15 分钟之内是否低于 85%,如果低于 85%,eureka 会将这些实例保护起来,让这些实例不会过期”(需要注意这句话的前半部分,我们这篇文章将用源码推翻它),其实就是某段时间内收到心跳数量低于期望心跳数量85%,就会触发这个自我保护机制,这个时候,服务实例将不会被过期。

2.实现原理

一个核心原理:实际收到的心跳 小于 我心里能接受的最小心跳数 ,这个时候就会触发eureka 自我保护机制

两个概念(都很好理解):

  1. 一个是期望心跳数期望心跳数与服务实例多少是有关系的,比如说我注册表中有10个实例,然后每30s发送一次心跳(也就是续约),那么我这个每分钟期望心跳数就是 10 * 2 ,也就是我期望收到 20个心跳。
  2. 还有一个是我心里能接受的最小心跳数,网络环境这么复杂,你总不能让这些实例的心跳一个都不少吧,所以这里就出现了一个心里能接受的最小心跳数(心跳数阈值),它的计算方式就是 期望心跳数 * 0.85 (这个0.85 是默认的,是不是与上面那个85% 就对起来了)

有了 期望心跳数最小心跳数阈值,我们现在还差的是啥?
那就是统计收到的心跳数
比如说我专门有个计数器,然后以一个时间单位为一个窗口,就是统计每分钟收到的心跳次数(续约次数),其实光有收集当前分钟心跳数的计数器还不够,总不能拿当前分钟的心跳数与心里能接受的最小心跳数 做比较吧,那分钟开始的时候,肯定会触发这个自我保护机制(毕竟分钟开始的时候,没收到多少心跳)
这个时候一个计数器就不够了,还得需要一个,一个用于统计当前分钟收到的心跳数量一个用于存储上一分钟收到的心跳数,然后拿上一分钟收到的心跳数 与 心里能接受的最小心跳数 做比较,然后小于心里能接受的最小心跳数,就触发自我保护机制。
有了2个心跳计数器,还不够,还缺一个定时器,然后每分钟执行一次将 当前分钟收集到的心跳数存储上一分钟这个计数器上,然后将当前分钟计数器清0,表示开启新一轮的计数。

到这里,其实大体上就已经讲清楚了,但是还有几个点

  1. 最开始的时候期望心跳数是怎么出来的?
  2. 服务注册,服务下线 这个期望心跳数是怎样变化的?

现在解答一下:

  1. 最开始的时候,eureka server 启动会去其他server节点上拉取注册表,如果拉到注册表的话,遍历注册表里面的实例信息,然后挨个 按照注册的方式 注册到本机的注册表上,它会有一个返回一个注册多少实例count ,接着就是按照这个count *2 初始化的。
  2. 服务注册的时候,期望心跳数 +2 ,我多一个实例,一个实例每分钟默认发送2个心跳,这个时候就会+2 。服务下线的时候 期望心跳数 -2,然后心里能接受的最小心跳数 也会重新算一遍。
    好了,以上就是eureka 自我保护机制的实现原理了,只不过我用自己的话将代码翻译了一遍,如果还是不能明白,可以看一下第4节的那张图。接下来我们就要看一下源码了

3.源码解析

3.1期望心跳数我心里能接受的最小心跳数

先看一下关于 期望心跳数我心里能接受的最小心跳数 变量的定义
在 eureka-core 项目的注册表抽象类AbstractInstanceRegistry 中定义了这两个变量
在这里插入图片描述
接着看下初始化,服务注册,服务下线 期望心跳数我心里能接受的最小心跳数 变动。

3.1.1 初始化

eureka server在启动的时候,会进行初始化,它会到其他server 节点上同步一下注册表,然后注册到自己本地的注册表中,这个时候会返回一个同步数量count,后面注册表中的这两个变量会根据这个count计算出来
在这里插入图片描述
先是同步注册表,返回同步实例的数量。
在这里插入图片描述
接着就是调用注册表的 openForTraffic 方法,初始化这两个变量,改变applicationInfoManager 为up状态,完成注册表 初始化之后的动作
这里期望心跳数 直接就是同步实例数量*2 ,我心里能接受的最小心跳数期望心跳数 * 0.85(默认是0.85)

3.1.2 服务注册

在注册表抽象类AbstractInstanceRegistry 中的register方法中
在这里插入图片描述
可以看到 期望心跳数 +2 ,然后 我心里能接受的最小心跳数 重新算了一遍。

3.1.3 服务下线

这个是在注册表抽象类AbstractInstanceRegistry 的实现类PeerAwareInstanceRegistryImpl中的cancel 方法做的处理
在这里插入图片描述
它这里有个很大的问题就是这个+2 和-2 直接写死了,如果我心跳间隔改变了,这个时候不是30s发一次心跳了,变成了1分钟一次,然后eureka 就会一直处于自我保护机制中。

3.2 关于计数器
3.2.1 计数器初始化

eureka server 启动的时候,会执行一堆初始化,这个是在EurekaBootstrap中的,之后就是创建注册表,在注册表抽象类AbstractInstanceRegistry的构造方法中创建了这个计数器。
在这里插入图片描述
然后再初始化最后面调用了一下注册表的openForTraffic 方法,初始化期望心跳数我心里能接受的最小心跳数 这两个变量,改变applicationInfoManager 为up状态,完成注册表 初始化之后的动作,就在注册表 初始化之后的动作里面启动了计数器。
在这里插入图片描述
先来看下 MeasuredRate 这类中关键的几个成员变量:
在这里插入图片描述
它的start 方法其实就是启动这个定时任务
在这里插入图片描述
它这里是1分钟执行一次,就是sampleInterval 这个成员决定的,这个成员实在创建这个对象的传过来的,然后就是1分钟。
看下这个画红框的代码,其实就是将 当前分钟收集到的心跳数存储上一分钟这个计数器 lastBucket上,然后将当前分钟计数器 currentBucket清0,表示开启新一轮的计数。

3.2.2 心跳计数

其实每收到一个心跳(续约),都会给currentBucket 这变量自增1,我们看一下代码
在这里插入图片描述
在这里插入图片描述

3.3 关于自我保护模式的判断

在这里插入图片描述
在这里插入图片描述
上面就是就是自我保护模式的判断了,很清楚。

3.4 eureka web中那行大红字

在resource 的header.jsp中有这么几行
在这里插入图片描述
先是调用注册表的isBelowRenewThresold 方法判断是不是触发了自我保护机制
在这里插入图片描述
要想显示那行触发自我保护机制的大红字,需要 系统启动了5分钟以上,而且触发了自我保护机制,其实还需要isSelfPreservationModeEnabled 这个参数是true,这样子才会显示这个触发自我保护的大红字

3.5 触发自我保护对过期下线的影响

在注册表 postInit方法中,其实还启动一个定时任务,扫描那些过期的实例,然后进行服务下线动作,这里主要是介绍触发自我保护对过期下线的影响,不会深入剖析服务扫描下线这一堆的东西
在这里插入图片描述
这个定时任务是1分钟执行一次的。
在这里插入图片描述
进入这个evict 方法中。
在这里插入图片描述
如果触发了自我保护机制就不往下走了,其实下面就是扫描注册表中所有的实例信息,然后看看过没过期,过期就走服务下线逻辑。我们来看下这个isLeaseExpirationEnabled方法的实现。
在这里插入图片描述
如果没有启用这个自我保护机制的话,直接通过,走下面服务扫描过期的逻辑。如果启用了,还用判断 收到的心跳数大于期望最小心跳数 ,这个时候才能继续往下走,如果是小于的话,就是触发自我保护机制了,就不会再往下走了,也就是不会再扫描下线过期的实例。

4.一张图

在这里插入图片描述

总结

本篇文章主要是介绍了eureka自我保护机制的实现原理,不管是语言描述,还是代码剖析,都已经很明了了。我没有找到所谓的 15分钟内
心跳失败比例小于85% 就是触发自我保护机制,在源码中,我只分析出 你上一分钟心跳数 低于 期望心跳数的85% 就会触发自我保护机制,我不知道他们所谓的15分钟是哪里的,我只相信源码出真知,然后这里还发现计算期望心跳数 的硬编码问题,如果你心跳间隔改变了,不是30s了,就很容易出现bug。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐