深度解析Eureka的自我保护机制
原创不易,转载请注明出处文章目录1.自我保护机制介绍2.实现原理3.源码解析总结1.自我保护机制介绍2.实现原理3.源码解析总结
原创不易,转载请注明出处
文章目录
1.自我保护机制介绍
想必用过eureka 的同学都在eureka web控制台见过这么一行大红字,我现在还能想起来第一次用eureka 出现这个的心里的那种感觉,什么东西,这么显眼,然后就很懵逼,立马搜了一下这个是什么鬼,就搜到说eureka自我保护机制触发了,说是 “eureka 在运行期间会去统计心跳失败比例在 15 分钟之内是否低于 85%,如果低于 85%,eureka 会将这些实例保护起来,让这些实例不会过期”(需要注意这句话的前半部分,我们这篇文章将用源码推翻它),其实就是某段时间内收到心跳数量低于期望心跳数量85%,就会触发这个自我保护机制,这个时候,服务实例将不会被过期。
2.实现原理
一个核心原理:实际收到的心跳
小于 我心里能接受的最小心跳数
,这个时候就会触发eureka 自我保护机制
两个概念(都很好理解):
- 一个是
期望心跳数
,期望心跳数
与服务实例多少是有关系的,比如说我注册表中有10个实例,然后每30s发送一次心跳(也就是续约),那么我这个每分钟期望心跳数
就是 10 * 2 ,也就是我期望收到 20个心跳。 - 还有一个是
我心里能接受的最小心跳数
,网络环境这么复杂,你总不能让这些实例的心跳一个都不少吧,所以这里就出现了一个心里能接受的最小心跳数(心跳数阈值
),它的计算方式就是期望心跳数 * 0.85
(这个0.85 是默认的,是不是与上面那个85% 就对起来了)
有了 期望心跳数
与 最小心跳数阈值
,我们现在还差的是啥?
那就是统计收到的心跳数
比如说我专门有个计数器,然后以一个时间单位为一个窗口,就是统计每分钟收到的心跳次数(续约次数),其实光有收集当前分钟心跳数的计数器还不够,总不能拿当前分钟的心跳数与心里能接受的最小心跳数 做比较吧,那分钟开始的时候,肯定会触发这个自我保护机制(毕竟分钟开始的时候,没收到多少心跳)
这个时候一个计数器就不够了,还得需要一个,一个用于统计当前分钟收到的心跳数量
,一个用于存储上一分钟收到的心跳数
,然后拿上一分钟收到的心跳数 与 心里能接受的最小心跳数 做比较,然后小于心里能接受的最小心跳数,就触发自我保护机制。
有了2个心跳计数器,还不够,还缺一个定时器
,然后每分钟执行一次将 当前分钟收集到的心跳数
给 存储上一分钟这个计数器
上,然后将当前分钟计数器
清0,表示开启新一轮的计数。
到这里,其实大体上就已经讲清楚了,但是还有几个点
- 最开始的时候
期望心跳数
是怎么出来的? - 服务注册,服务下线 这个
期望心跳数
是怎样变化的?
现在解答一下:
- 最开始的时候,eureka server 启动会去其他server节点上拉取注册表,如果拉到注册表的话,遍历注册表里面的实例信息,然后挨个 按照注册的方式 注册到本机的注册表上,它会有一个返回一个注册多少实例count ,接着就是按照这个count *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。
更多推荐
所有评论(0)