Eureka 源码解析 —— 应用实例注册发现(七)之增量获取
来源:互联网 发布:mac的终端是什么 编辑:程序博客网 时间:2024/06/05 11:14
摘要: 原创出处 http://www.iocoder.cn/Eureka/instance-registry-fetch-delta/ 「芋道源码」欢迎转载,保留摘要,谢谢!
本文主要基于 Eureka 1.8.X 版本
- 1. 概述
- 2. 应用集合一致性哈希码
- 2.1 计算公式
- 2.2 合理性
- 3. Eureka-Client 发起增量获取
- 3.1 合并应用集合
- 4. Eureka-Server 接收全量获取
- 3.1 接收全量获取请求
- 3.2 最近租约变更记录队列
- 3.3 缓存读取
- 请支持正版。下载盗版,等于主动编写低级 BUG 。
- 程序猿DD —— 《Spring Cloud微服务实战》
- 周立 —— 《Spring Cloud与Docker微服务架构实战》
- 两书齐买,京东包邮。
- Eureka-Server 近期变化( 注册、下线 )的应用集合
- Eureka-Server 应用集合一致性哈希码
- 使用每个应用实例状态(
status
) + 数量(count
)拼接出一致性哈希码。若数量为 0 ,该应用实例状态不进行拼接。状态以字符串大小排序。 - 举个例子,8 个 UP ,0 个 DOWN ,则
appsHashCode = UP_8_
。8 个 UP ,2 个 DOWN ,则appsHashCode = DOWN_2_UP_8_
。 实现代码如下:
调用
#populateInstanceCountMap()
方法,计算每个应用实例状态的数量。实现代码如下:- 计数那块代码,使用 Integer 即可,无需使用 AtomicInteger 。
调用
#getReconcileHashCode()
方法,计算hashcode
。实现代码如下:
- 正常情况下,Eureka-Client 从 Eureka-Server 获取到完整的增量变化并合并,此时应用情况如下表格所示,两者是一致的,一致性哈希算法合理。
- 异常情况下【1】,变更记录队列全部过期。那 Eureka-Client 从 Eureka-Server 获取到空的增量变化并合并,此时应用情况如下表格所示,两者应用是不相同的, 一致性哈希值却是相等的,一致性哈希算法不合理。
- 异常情况下【2】,变更记录队列部分过期,例如应用A 和 应用B 都剩余 w 条变更记录。那 Eureka-Client 从 Eureka-Server 获取到部分的增量变化并合并,两者应用是不相同的,此时应用情况如下表格所示,一致性哈希值却是相等的,一致性哈希算法不合理。
- 模拟场景:异常情况【1】,m = n = c = 1 。简单粗暴。
- 特别配置
eureka.retentionTimeInMSInDeltaQueue = 1
,变更记录队列每条记录存活时长 1 ms。用以实现 Eureka-Client 请求不到完整的增量变化。eureka.deltaRetentionTimerIntervalInMs = 1
,变更记录队列每条记录过期定时任务执行频率 1 ms。用以实现 Eureka-Client 请求不到完整的增量变化。eureka.shouldUseReadOnlyResponseCache = false
,禁用响应缓存的只读缓存。用以避免等待缓存刷新。eureka.waitTimeInMsWhenSyncEmpty = 1
,
- 实验过程
- 00:00 启动 Eureka-Server
- 00:30 启动应用A ,向 Eureka-Server 注册
- 01:00 启动 Eureka-Client ,向 Eureka-Server 获取注册信息,等待获取到应用A
- 01:30 关闭应用A 。立即启动应用B ,向 Eureka-Server 注册
- 等待 5 分钟,Eureka-Client 无法获取到应用B
- 此时应用情况如下表格所示,两者应用是不相同的,一致性哈希值却是相等的,一致性哈希算法不合理。
- 第一种,修改计算公式
appsHashCode = MD5(${app_name}_${instance_id}_${status}_${count}_)
,增加对应用名和应用实例编号敏感。 - 第二种,每 N 分钟进行一次全量获取注册信息。
第 4 至 9 行 :请求增量获取注册信息,实现代码如下:
- 调用
AbstractJerseyEurekaHttpClient#getApplicationsInternal(...)
方法,GET 请求 Eureka-Server 的apps/detla
接口,参数为regions
,返回格式为 JSON ,实现增量获取注册信息。
- 调用
第 11 至 15 行 :增量获取失败,调用
#getAndStoreFullRegistry()
方法,全量获取注册信息,并设置到本地缓存。该方法在 《Eureka 源码解析 —— 应用实例注册发现(六)之全量获取》「2.4.1 全量获取注册信息,并设置到本地缓存」 有详细解析。- 第 16 至 35 行 :处理增量获取的结果。
- 第 16 行 :TODO[0025] :并发更新的情况???
- 第 19 行 :TODO[0025] :并发更新的情况???
- 第 21 行 :调用
#updateDelta(...)
方法,将变化的应用集合和本地缓存的应用集合进行合并。 - 第 31 至 35 行 :一致性哈希值不相等,调用
#reconcileAndLogDifference()
方法,全量获取注册信息,并设置到本地缓存,和#getAndStoreFullRegistry()
基本类似。- 第 33 行 :配置
eureka.printDeltaFullDiff
,是否打印增量和全量差异。默认值 :false
。从目前代码实现上来看,暂时没有生效。注意 :开启该参数会导致每次增量获取后又发起全量获取,不要开启。
- 第 33 行 :配置
- 第 6 至 15 行 :TODO[0009]:RemoteRegionRegistry
第 18 至 24 行 :添加( ADDED )应用实例时,调用
Application#addInstance(...)
方法,实现代码如下:第 25 至 32 行 :修改( MODIFIED )应用实例时,同样调用
Application#addInstance(...)
方法。第 33 至 40 行 :删除( DELETED )应用实例时,调用
Application#removeInstance(...)
方法,实现代码如下:第 47 行 :调用
Applications#shuffleInstances(...)
方法,根据配置eureka.shouldFilterOnlyUpInstances = true
( 默认值 :true
) 过滤只保留状态为开启( UP )的应用实例,并随机打乱应用实例顺序。打乱后,实现调用应用服务的随机性。代码比较易懂,点击链接查看方法实现。- 第 49 至 53 行 :TODO[0009]:RemoteRegionRegistry
- 和 《Eureka 源码解析 —— 应用实例注册发现(六)之全量获取》「3.1 接收全量获取请求」 类似,就不重复啰嗦啦。
- 点击 链接 查看该方法的带中文注释代码。
- 当应用实例注册、下线、状态变更时,创建最近租约变更记录( RecentlyChangedItem ) 到队列。
后台任务定时顺序扫描队列,当
lastUpdateTime
超过一定时长后进行移除。实现代码如下:- 配置
eureka.deltaRetentionTimerIntervalInMs
, 移除队列里过期的租约变更记录的定时任务执行频率,单位:毫秒。默认值 :30 * 1000 毫秒。 - 配置
eureka.retentionTimeInMSInDeltaQueue
,租约变更记录过期时长,单位:毫秒。默认值 : 3 60 1000 毫秒。
- 配置
- 第 2 至 3 行 :添加增量获取次数到监控。配合 Netflix Servo 实现监控信息采集。
- 第 4 行 :初始化变化( 增量 )的应用集合(
apps
)。 - 第 9 行 :获取写锁。在 《Eureka源码解析 —— 应用实例注册发现 (九)之岁月是把萌萌的读写锁》 详细解析。
- 第 11 至 13 行 :获取最近租约变更记录队列(
最近租约变更记录队列
)。 - 第 14 至 27 行 :拼装变化的应用集合(
apps
)。 - 第 29 至 44 行 :TODO[0009]:RemoteRegionRegistry
- 第 46 至 48 行 :调用
#getApplications(...)
方法,获取全量应用集合(allApps
),在 《Eureka 源码解析 —— 应用实例注册发现(六)之全量获取》「3.3.1 获得注册的应用集合」 有详细解析。后通过allApps
计算一致性哈希值。通过这个全量应用集合的哈希值,Eureka-Client 获取到增量应用集合并合并后,就可以比对啦。 - 第 51 行 :释放写锁。
1. 概述
本文主要分享 Eureka-Client 向 Eureka-Server 获取增量注册信息的过程。
前置阅读:《Eureka 源码解析 —— 应用实例注册发现(六)之全量获取》
FROM 《深度剖析服务发现组件Netflix Eureka》
Eureka-Client 获取注册信息,分成全量获取和增量获取。默认配置下,Eureka-Client 启动时,首先执行一次全量获取进行本地缓存注册信息,而后每 30 秒增量获取刷新本地缓存( 非“正常”情况下会是全量获取 )。
本文重点在于增量获取。
推荐 Spring Cloud 书籍:
2. 应用集合一致性哈希码
Applications.appsHashCode
,应用集合一致性哈希码。
增量获取注册的应用集合( Applications ) 时,Eureka-Client 会获取到:
Eureka-Client 将变化的应用集合和本地缓存的应用集合进行合并后进行计算本地的应用集合一致性哈希码。若两个哈希码相等,意味着增量获取成功;若不相等,意味着增量获取失败,Eureka-Client 重新和 Eureka-Server 全量获取应用集合。
Eureka 比较应用集合一致性哈希码,和日常我们通过哈希码比较两个对象是否相等类似。
2.1 计算公式
appsHashCode = ${status}_${count}_
2.2 合理性
本小节,建议你理解完全文后,再回到此处
本小节,建议你理解完全文后,再回到此处
本小节,建议你理解完全文后,再回到此处
笔者刚看完应用集合一致性哈希算法的计算公式,处于一脸懵逼的状态。这么精简的方式真的能够校验出数据的一致性么?不晓得有多少读者跟笔者有一样的疑惑。下面我们来论证该算法的合理性( 一本正经的胡说八道 )。
一致性哈希值通过状态 + 数量来计算,那么是不是可能状态总数是一样多,实际分布在不同的应用?那么我们列举模型如下:
如果此时应用A 下线了 c 个原应用实例,应用B 注册了 c 个信应用实例,那么处于 UP 状态的数量仍然是 m + n 个。
What ? 从异常情况【1】【2】可以看到,一致性哈希算法竟然是不合理的,那么我们手动来做一次最精简的实验。实验如下:
��结论��
当然排除掉特别极端的场景,Eureka-Client 从 Eureka-Server 因为网络异常导致一直同步不到增量变化,又恰好应用关闭和开启满足状态统计数量。另外,变更记录队列记录过期时长为 300 秒,增量获取频率为 30 秒,获取的次数有 10 次左右。所以,应用集合一致性哈希码在绝大多数场景是合理的。笔者的YY,解决这个极小场景有如下方式:
ps :笔者怀着忐忑的心写完了这个小节,如果有不合理的地方,又或者有不同观点的胖友,欢迎一起探讨。谢谢。
TODO[0027][反思]:应用集合一致性哈希算法。
3. Eureka-Client 发起增量获取
在 《Eureka 源码解析 —— 应用实例注册发现(六)之全量获取》「2.4 发起获取注册信息」 里,调用 DiscoveryClient#getAndUpdateDelta(...)
方法,增量获取注册信息,并刷新本地缓存,实现代码如下:
3.1 合并应用集合
调用 #updateDelta(...)
方法,将变化的应用集合和本地缓存的应用集合进行合并。实现代码如下:
4. Eureka-Server 接收全量获取
3.1 接收全量获取请求
com.netflix.eureka.resources.ApplicationsResource
,处理所有应用的请求操作的 Resource ( Controller )。
接收增量获取请求,映射 ApplicationsResource#getContainers()
方法。
3.2 最近租约变更记录队列
AbstractInstanceRegistry.recentlyChangedQueue
,最近租约变更记录队列。实现代码如下:
3.3 缓存读取
在 《Eureka 源码解析 —— 应用实例注册发现(六)之全量获取》「3.3 缓存读取」 里,在 #generatePayload()
方法里,调用 AbstractInstanceRegistry#getApplicationDeltas(...)
方法,获取近期变化的应用集合,实现代码如下:
- Eureka 源码解析 —— 应用实例注册发现(七)之增量获取
- Eureka 源码解析 —— 应用实例注册发现(六)之全量获取
- Eureka 源码解析 —— 应用实例注册发现(一)之注册
- Eureka 源码解析 —— 应用实例注册发现(二)之续租
- Eureka 源码解析 —— 应用实例注册发现(三)之下线
- Eureka 源码解析 —— 应用实例注册发现(四)之自我保护机制
- Eureka 源码解析 —— 应用实例注册发现(五)之过期
- Eureka 源码解析 —— 应用实例注册发现(八)之覆盖状态
- Eureka 源码解析 —— Eureka源码解析 —— 应用实例注册发现 (九)之岁月是把萌萌的读写锁
- Spring-cloud & Netflix 源码解析:Eureka 服务注册发现接口 ****
- Eureka 源码解析 —— Eureka-Client 初始化(一)之 EurekaInstanceConfig
- Eureka 源码解析 —— Eureka-Client 初始化(二)之 EurekaClientConfig
- Eureka 源码解析 —— Eureka-Client 初始化(三)之 EurekaClient
- Eureka 源码解析 —— Eureka-Server 启动(一)之 ServerConfig
- Eureka 源码解析 —— Eureka-Server 启动(二)之 EurekaBootStrap
- spring could 之服务的注册与发现(Eureka)
- SpringCloud——Eureka服务注册和发现
- SpringCloud——Eureka服务注册和发现
- java——线程生命周期waiting状态和blocked状态的区别
- PyImport_ImportModule
- 关于SparkStreaming运行案例时控制台不打印的问题
- 数据库(表结构)设计技巧及注意事项
- JAVA NIO 例子
- Eureka 源码解析 —— 应用实例注册发现(七)之增量获取
- Parameter 'userName' not found. Available parameters are [0, 1, 2, param3, param1, param2]
- 数据结构链表习题2.27,假设以两个元素依值递增有序排列的线性表A和B分别表示两个集合,现要求另辟空间构成一个顺序链表
- CSDN 第一篇博文
- linux下启动svn实例
- Spring Cloud (19) | Eureka Server 高可用服务注册中心
- Django实现CAS+OAuth2
- demon14.5-14.6
- 合并排序,合为重