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微服务架构实战》

  • 两书齐买,京东包邮。

推荐 Spring Cloud 视频

  • Java 微服务实践 - Spring Boot

  • Java 微服务实践 - Spring Cloud

  • Java 微服务实践 - Spring Boot / 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(...) 方法,该方法执行过程中,需要保证 recentlyChangedQueue 和 registry 共享变量的应用实例的状态一致,不然返回的增量应用实例集合的状态是不准确的。此时能够达到该效果,必须让 #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 提问,并且做好了获得不到答案的准备,结果无意中的抠脚( 请允许我热爱抠脚给我带来的灵感 )解答了自己的疑惑。

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

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


©著作权归作者所有:来自51CTO博客作者mb5ff80520dfa04的原创作品,如需转载,请注明出处,否则将追究法律责任

更多相关文章

  1. DoDAF2.0方法论探究
  2. http协议请求方法都有哪些?网络安全学习提升
  3. 【前端词典】8 个提高 JS 性能的方法
  4. AngularJS 日期时间选择组件(附详细使用方法)
  5. 华为交换机console口设置密码及状态查看命令
  6. 5 种方法教你用Python玩转histogram直方图
  7. IDEA Debug 无法进入断点的解决方法
  8. libp2p-rs kad 使用及调试方法
  9. 只会爬虫不会反爬虫?动图详解利用 User-Agent 进行反爬虫的原理和

随机推荐

  1. 小胖加入Android Fans的 大军了 呵呵
  2. 在android的java代码中自定义log
  3. Android热修复(一):底层替换、类加载原理总
  4. Android(安卓)长按电源键关机整个流程小
  5. 松下将在日本推出 7 寸和 10 寸企业用三
  6. Android 的网络编程
  7. init进程【4】——属性服务
  8. android系统体系结构
  9. Android(安卓)拼接两个图片
  10. Android 下使用 JSON 实现 HTTP 请求