转载

注册中心 Eureka 源码解析 —— Eureka源码解析 —— 应用实例注册发现 (九)之岁月是把萌萌的读写锁

摘要: 原创出处 http://www.iocoder.cn/Eureka/instance-registry-read-write-lock/ 「芋道源码」欢迎转载,保留摘要,谢谢!

  • 1. 概述
  • 2. 读写锁
  • 666. 彩蛋

注册中心 Eureka 源码解析 —— Eureka源码解析 —— 应用实例注册发现 (九)之岁月是把萌萌的读写锁

������关注 微信公众号:【芋道源码】 有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有 源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言 将得到 认真 回复。 甚至不知道如何读源码也可以请教噢
  4. 新的 源码解析文章 实时 收到通知。 每周更新一篇左右
  5. 认真的 源码交流微信群。

1. 概述

本文主要分享 Eureka 注册中心的那把读写锁 ,让我瘙痒难耐,却不得其解。在某次意外的抠脚的一刻( 笔者不抽烟,如果抽烟的话,此处应该就不是抠脚了 ),突然顿悟,爽,这好比… 比喻有点猥琐,笔者就省略 100 字。

不瞎比比,上代码:

public abstract class AbstractInstanceRegistry implements InstanceRegistry{

 private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
 private final Lock read = readWriteLock.readLock();
 private final Lock write = readWriteLock.writeLock();

 // ... 省略其他代码

}

推荐 Spring Cloud 书籍:

  • 请支持正版。下载盗版, 等于主动编写低级 BUG
  • 程序猿DD —— 《Spring Cloud微服务实战》
  • 周立 —— 《Spring Cloud与Docker微服务架构实战》
  • 两书齐买,京东包邮。

2. 读写锁

我们把设计到读写锁的方法整理如下:

方法 读锁 写锁 不使用
#register(...)
#cancel(...)
#evict(...)
#renew(...)
#statusUpdate(...)
#deleteStatusOverride(...)
#getApplicationDeltasFromMultipleRegions(...)
#getApplicationsFromMultipleRegions(...)

是否看到这读写感到几丝诡异的味道?OK,我们把问题梳理如下:

  • A. 为什么 #register(...) / #cancel(...) / #evict(...) / #statusUpdate(...) / #deleteStatusOverride(...)写操作 使用 读锁
  • B. 为什么 #renew(...) 写操作 不使用
  • C. 为什么 #getApplicationDeltasFromMultipleRegions(...) 读操作 使用 写锁
  • D. 为什么 getApplicationsFromMultipleRegions(...) 读操作 不使用

先解释 A + C :

我们来回想下,在 Eureka 应用集合一致性哈希码的公式: appsHashCode = ${status}_${count}_ 。( 不了解的同学可以加载下 《Eureka 源码解析 —— 应用实例注册发现(七)之增量获取》「 2. 应用集合一致性哈希码 」 )

应用实例的数量和状态都会影响 哈希码 的计算结果。也就是说,上述 前六个 ( 包括不使用锁的 #renew(...) 方法 )方法的调用都会影响哈希码。

我们把目光移向唯一使用 写锁#getApplicationDeltasFromMultipleRegions(...) 方法,该方法执行过程中,需要保证 recentlyChangedQueueregistry 共享变量的 应用实例的状态一致 ,不然返回的增量应用实例集合的状态是不准确的。此时能够达到该效果,必须让 #getApplicationDeltasFromMultipleRegions(...) 和前六个方法 互斥 。方案如下:

  • a. 全部 synchronized
  • b. #getApplicationDeltasFromMultipleRegions(...) 使用 读锁 ,前六个方法使用 写锁
  • c. #getApplicationDeltasFromMultipleRegions(...) 使用 写锁 ,前六个方法使用 读锁

Eureka 选择了 方案c ,原因如下:

  • a. 性能太差
  • b. 前六个方法使用 写锁 ,势必冲突太大,虽然读肯定比写多。
  • c. #getApplicationDeltasFromMultipleRegions(...) 使用 写锁 ,配合 ResponseCache ,即减少了 写锁 使用的频率,每次缓存过期才使用,又避免了前六个方法因为 方案b 中的 写锁 导致互斥。( 不了解 ResponseCache 的同学可以加载下 《Eureka 源码解析 —— 应用实例注册发现(六)之全量获取》「 3.2 响应缓存 ResponseCache 」 )

再解释 D

#getApplicationsFromMultipleRegions(...) 方法的逻辑,只依赖 registry 共享变量,不存在应用实例的状态一致的困扰,所以不使用锁。

最后解释 B

#renew(...) 方法的逻辑,虽然会影响应用实例的状态,但是是极小概率,考虑到它调用的比较频繁,比起因为锁给这个方法带来的性能降低,不如返回的结果暂时不够准确。( 想了解极小概率发生原因的同学可以加载 《Eureka 源码解析 —— 应用实例注册发现(八)之覆盖状态》「 4.3 续租场景 」 )

TODO [0029] 读写锁

笔者路上突然又想了问题,可能不是上述原因,可能和 ResponseCache 有关系,参见 #invalidateCache(...) 方法的每次调用。也就是说,这个读写锁是针对 ResponseCache 的读写锁。

666. 彩蛋

开森 !

本来以为需要跟 Eureka 官方提交 issue 提问,并且做好了获得不到答案的准备,结果无意中的抠脚( 请允许我热爱抠脚给我带来的灵感 )解答了自己的疑惑。

岁月是把纠结而又萌萌哒的锁,你不知道你的困扰,哪天不经意的被打开。

愿大喜大悲,不枉仅知的这一生。

原文  http://www.iocoder.cn/Eureka/instance-registry-read-write-lock/?csdn&2018-01-18
正文到此结束
Loading...