一、消息存储

1
消息存储整体架构
CommitLog:消息主体以及元数据的存储主体(可包含多个topic)
-- 定长,文件默认1G,文件名为20位,为偏移量的数值来命名。文件顺序写(性能几乎等于内存读)。
---- 比如第一个文件名:00000000000000000000,由于第一个文件大小为
1G=1073741824
,第二个文件名应该为:0000000000
1073741824
。
ConsumeQueue:消息消费队列,提升消息消费性能(存储了待消费消息偏移量),
等同于基于topic的commitLog索引文件
-- 具体存储的结构:偏移量offset、消息大小size、消息tag hashcode值
-- 具体存储路径为:$HOME/store/consumequeue/{topic}/{queueId}/{fileName}
-- 文件大小:约5.72M

IndexFile:索引文件,提供了
key或时间 区间查消息的方法
-- 具体存储的结构:偏移量offset、消息大小size、消息tag hashcode值
-- 具体存储路径为:$HOME/store/consumequeue/{topic}/{queueId}/{fileName}
-- 文件大小:400M,可存储2000w个,一个大小20字节
总结:Broker 单实例下所有队列共用一个CommitLog文件,采用同步/异步持久化到commitlog。
2 页缓存与内存映射
PageCache:os对文件缓存。
异步线程将PageCache写入磁盘,并有
预读数据到PageCache,加速读写。另外ConsumerQueue采用的是
顺序读,用
偏移量offset取数,读取效率接近内存读。
Mmap:通过MappedByteBuffer进行读写,减少一次从内核缓冲区到用户缓冲区的拷贝。
3 消息刷盘
同步刷盘:写入到磁盘后才返回ack。
异步刷盘:只需要写入PageCache就返回ack,然后有个异步线程会把PageCache刷入磁盘中。
二、通信机制

rocketmq-remoting 模块是生产者、消费者、Broker、NameServer的基础通信模块,其组成结构大致如下:

备注:消息长度(4字节) + [序列化类型(1字节) + 消息头长度(3字节)](4字节) + 消息头数据 + 消息主体数据
支持的通信方式
三种方式:同步、异步、单向,其中单向不需要返回ack确认
其RPC底层采用Netty作为底层通信库
三、消息过滤
有两种方式:Tag、SQL92 -- Broker有一个MessageFilter用于过滤
Tag过滤方式:
指定Topic,还能指定若干个tag,然后封装成一个请求体,consumer发一个pull请求给Broker,然后到了Broker后会从ConsumeQueue中取到对应tag hash,然后
比较hashcode和tag是否一致,两个都一致就过滤成功(hashcode不同内容数据可能会冲突一样),进行消费。
SQL92过滤方式:跟上面基本一样,只是过滤方式变成sql语法过滤,比如"id >= 0 and id <= 10"
四、负载均衡
Producer负载:从缓存中获取路由信息(如果没有则请求NameServer获取)。
-- 并有一个
容错策略MQFaultStrategy(latencyFaultTolerance机制 -- 消息发送高可用核心):会按一定的
时间退避,latency延迟>550ms,就退避3000ms,延迟>1000ms,就退避60000ms
Consumer负载:先拉一批,然后放到消息消费线程池。
-- 有环形平均和平均负载均衡算法,核心是保证一个队列同一时间只被一个消费者消费,一个消费者可以消费多个队列。
五、分布式事务
RocketMQ实现可靠最终一致性方案过程:
1 发送half message消息给RocketMQ
2 MQ返回message success回去
3 如果第1、2因为某种原因(比如网络原因)发送或接收失败,服务则会重试发送
4 然后是一些业务逻辑的处理,比如下订单、扣减库存、发货,走完这些核心链路之后
5 如果业务逻辑处理成功,就commit 消息,如果失败了就回滚消息
6 如果上面因为网络原因,commit/rollback没有被MQ收到,过一段时间,MQ就会回调系统的接口,确认是成功还是失败,然后做一系列业务逻辑,然后返回commit/rollback
7 消费者消费消息,如果消费者宕机了,MQ过一段时间会重新投递消息,直到消费者ACK后,然后回调生产者服务接口来更新生产者的对应的数据状态。