⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

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


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

  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 书籍

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,原因如下:


再解释 D

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


最后解释 B

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


TODO [0029] 读写锁

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

666. 彩蛋

知识星球

开森 !

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

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

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

文章目录
  1. 1. 1. 概述
  2. 2. 2. 读写锁
  3. 3. 666. 彩蛋