读写锁在高性能消息中间件RocketMQ中的妙用

从源码角度研究RocketMQ不仅可以深入研究一款高性能中间件的实现原理,更是可以大大提示编码能力,更是能加强对高并发等基础框架的实战能力。

首先我们来看一下RocketMQ NameServer的路由注册机制,从中窥探读写锁的使用场景

1、RocketMQ Nameserver 路由注册机制

在这里插入图片描述

  1. Broker 每 30s 向 NameServer 发送心跳包,心跳包中包含主题的路由信息(主题的读写队列数、操作权限等),NameServer 会通过 HashMap 更新 Topic 的路由信息,并记录最后一次收到 Broker 的时间戳。

  2. NameServer 以每 10s 的频率清除已宕机的 Broker,NameServr 认为 Broker 宕机的依据是如果当前系统时间戳减去最后一次收到 Broker 心跳包的时间戳大于 120s。

  3. 消息生产者以每 30s 的频率去拉取主题的路由信息,即消息生产者并不会立即感知 Broker 服务器的新增与删除。

在上述场景中,Broekr 会定时向 NameServer 汇报路由信息,即更新操作,消费者、生产在拉取消息、发送消息时会经常去查询 NameServer 中的路由信息,即所谓的读操作。

在 Broker NameServer 中使用 HashMap 来存储 topic 的路由信息“众所周知”HashMap 在多线程环境中并不安全,多线程环境下,需要加锁。

如下伪代码:

public List getTopicInfo(String topicName) {
    
    
     synchronized(routerInfoHashMap) {
    
    
           return routerInfoHashMap.get(topicName);
     }
}
public void updateTopicInfo(String topicName,List ...) {
    
    
     synchronized(routerInfoHashMap) {
    
    
           return routerInfoHashMap.put(topicName,  ...;
     }
}

上面的代码有什么缺点呢?

试想一下,如果同一个时间,多个发送者,多个消费者都向nameserver查询topic的路由信息,你会发现所有的读请求都会排队执行其并发性能可想而知的低下,此时读写锁就派上用场了。

读写锁的使用如下:
在这里插入图片描述
在这里插入图片描述

上述代码来源于org.apache.rocketmq.namesrv.routeinfo.RouteInfoManager。

这样所有的读操作能并发执行,极大的提高了性能。

读写锁的规则:读锁与写锁互斥,读锁与读锁兼容,即可同时获取锁,举例如下:

例如同一个时间依次有线程 t1 t2 t3 申请读锁,t4,申请写锁,t5 申请读锁:

首先 t1,t2,t3 成功获取锁,t4,t5阻塞等待。

等t1,t2,t3释放锁后,t4获取锁,t5阻塞,然后t4释放锁,t5获取锁。

本文就介绍到这里了,读写锁的使用场景大家是否Get到了,如果本文对你有所帮助,希望一键三连表示支持。


见字如面,我是威哥,一个从普通二本院校毕业,从未曾接触分布式、微服务、高并发到通过技术分享实现职场蜕变,成长为RocketMQ社区优秀布道师、大厂资深架构师,出版《RocketMQ技术内幕》一书,在CSDN中记录了我的成长历程,欢迎大家关注,私信,一起交流进步。

分享笔者一个硬核的RocketMQ电子书:
在这里插入图片描述
获取方式:微信搜索【中间件兴趣圈】,回复RMQPDF即可获取。

猜你喜欢

转载自blog.csdn.net/prestigeding/article/details/115361473