1. 架构总览
RocketMQ架构上主要分为四部分,如上图所示:
- Producer:消息发布的角色,支持分布式集群方式部署。Producer通过MQ的负载均衡模块选择相应的Broker集群队列进行消息投递,投递的过程支持快速失败并且低延迟。
- Consumer:消息消费的角色,支持分布式集群方式部署。支持以push推,pull拉两种模式对消息进行消费。同时也支持集群方式和广播方式的消费,它提供实时消息订阅机制,可以满足大多数用户的需求。
- NameServer:NameServer是一个非常简单的Topic路由注册中心,其角色类似Dubbo中的zookeeper,支持Broker的动态注册与发现。主要包括两个功能:Broker管理,NameServer接受Broker集群的注册信息并且保存下来作为路由信息的基本数据。然后提供心跳检测机制,检查Broker是否还存活;路由信息管理,每个NameServer将保存关于Broker集群的整个路由信息和用于客户端查询的队列信息。然后Producer和Conumser通过NameServer就可以知道整个Broker集群的路由信息,从而进行消息的投递和消费。NameServer通常也是集群的方式部署,各实例间相互不进行信息通讯。Broker是向每一台NameServer注册自己的路由信息,所以每一个NameServer实例上面都保存一份完整的路由信息。当某个NameServer因某种原因下线了,Broker仍然可以向其它NameServer同步其路由信息,Producer,Consumer仍然可以动态感知Broker的路由的信息。
- BrokerServer:Broker主要负责消息的存储、投递和查询以及服务高可用保证
2. topic、borker与queue三者关系
三者关系如下图:
在图中,共有2个broker,2个topic,每个topic都有4个队列。在producer发送消息时,是发送到具体的队列上,consumer获取消息时,也是从队列上获取。
注:RocketMq
的 topic 可以在控制台手动创建,也可以自动创建(需要开启配置autoCreateTopicEnable=true
),官方建议生产环境下关闭自动创建。
3. 消息处理流程
RocketMq消息处理整个流程如下:
- 消息接收:消息接收是指接收
producer
的消息,处理类是SendMessageProcessor
,将消息写入到commigLog
文件后,接收流程处理完毕; - 消息分发:
broker
处理消息分发的类是ReputMessageService
,它会启动一个线程,不断地将commitLong
分到到对应的consumerQueue
,这一步操作会写两个文件:consumerQueue
与indexFile
,写入后,消息分发流程处理 完毕; - 消息投递:消息投递是指将消息发往
consumer
的流程,consumer
会发起获取消息的请求,broker
收到请求后,调用PullMessageProcessor
类处理,从consumerQueue
文件获取消息,返回给consumer
后,投递流程处理完毕。
4. 三高保证
4.1 高并发
- netty 高性能传输:
producer
、broker
、comsumer
之间使用netty通信,高性能传输;业务处理时,使用的是自定义的工作线程池,最终处理操作在NettyServerHandler
中丢给工作线程池。 - 自旋锁减少上下文切换:RocketMQ 的 CommitLog 为了避免并发写入,使用一个
PutMessageLock
。PutMessageLock
有 2个实现版本:PutMessageReentrantLock
和PutMessageSpinLock
。PutMessageReentrantLock
是基于 java 的同步等待唤醒机制;PutMessageSpinLock
使用 Java 的 CAS 原语,通过自旋设值实现上锁和解锁。RocketMQ 默认使用PutMessageSpinLock
以提高高并发写入时候的上锁解锁效率,并减少线程上下文切换次数。 - 顺序写文件:写入
commitLog
时,使用的是顺序写入,比随机写入的性能高很多,写入commitLog
时,并不是直接写入磁盘的,而是先写入PageCache
,最后由操作系统异步将PageCache
的数据刷到磁盘中 MappedFile
预热和零拷贝机制
:Linux 系统在写数据时候不会直接把数据写到磁盘上,而是写到磁盘对应的PageCache
中,并把该页标记为脏页。当脏页累计到一定程度或者一定时间后再把数据 flush 到磁盘(当然在此期间如果系统掉电,会导致脏页数据丢失)。- 多
broker
多Queue