主要整理关于RocketMQ的刷盘, 存储, 高可用, NameServer的一些基本知识

Broker消息存储结构解析

消息的存储结构-单队列


rocketmq根据RocketMQTemplate获取topic_java

消息的存储结构-多队列


rocketmq根据RocketMQTemplate获取topic_数据_02

 

生产者把消息投递到commit Log

commit Log用于存储实际消息的数据, 是一个物理文件的存储

然后会有多个Consume Queue用于存储消息在Commit Log中的位置信息,

每一个Topic下的每一个Message Queue都对应一个Consume Queue文件, 具体的存储位置可以在配置文件中配置

每台broker机器上的Commit Log被本机所有的Cunsume Queue所共享

可知RocketMq的存储是由Commit Log和Consume Queue相互配合完成的

RocketMQ的文件存储的写盘方式是顺序写随机读的机制, 消费端采用零拷贝的原理提升性能

RocketMQ消息存储的时候如何实现消息顺序写, 就是使用Consumer Queue记录消息的位置(偏移量)来做顺序写的.

也就是说Consume Queue里边记录了所有消息处理的实际的当前的位置, 实际情况Consumer Queue的文件比较小, 可以直接把Consume Queue直接读到内存中提升速度

为了保证Commit Log和Consumer Queue的一致性, 所以Commit Log也会存储Consumer Queue里边的偏移量位置信息, 保证了Consumer Queue的数据有丢失, 也可以通过Commit Log来进行数据恢复

同步刷盘与异步刷盘

RocketMQ消息存储:内存+磁盘存储,两种刷盘方式

异步刷盘

消息先写到内存中, 就会返回结果给Producer说成功了,

好处写性能高, 吞吐量高,

当内存堆积到一定量时, 触发顺序写机制, 快速的将内存的刷回磁盘

同步刷盘

阻塞线程, 立即通知刷盘线, 进行数据的刷盘, 写入磁盘后再唤醒等待的线程返回结果给Producer

 

同步复制与异步复制

同一组Broker有Master - Slave角色

异步复制

刷盘之后立即返回结果, 然后再异步的同步数据到Slave, 在比较苛刻的场景下Master突然挂掉了会造成数据丢失的情况

同步复制

确保Master和Slave的原子性, 进行顺序同时写, 一起成功或者一起失败, 同步完数据再返回结果, 好处保证数据了高可用性

实际生产环境中采用同步双写+异步复制的机制, 来提高性能又兼顾可用性.

 

高可用机制

Master - Slave高可用

Brokerld区分是Master还是Slave

Master读、写, Slave只读

当Master繁忙或者不可用时,可以自动切换到Slave读取消息

数据层面高可用: 将Topic的Message Queue创建在多个Broker组上, 目的当一个Broker挂了另一个还可以用, 使得数据高可用.

服务级别高可用: 当前RocketMQ不支持主从节点的切换,但是可以写一个脚本来定时监控Master和Slave节点的状态, 当发现Master超时不可用时, 就将Slave节点的配置改成Master, 然后重启Slave节点, 就会变成Master节点了

 

NameServer协调者

为什么需要NameServer

对于整个消息队列集群来讲, 整个系统由多个节点组成, 每个节点的角色都不相同(ip等), 包括元数据信息也会随时变动, 比如配置项, 配置信息, topic路由信息, 队列数量等等. 如果新加Producer或者Consumer怎么进行连接配置, 如果Topic信息发生变更, 怎么通知Producer或者Consumer, 所以需要NameServer来对整合整个集群的状态

NameServer是整个集群的状态服务器

集群中的各个组件都能通过NameServer来了解全局的配置情况, 配置信息, 节点状态, 所以Master和Slave都需要定时的向NameServer来上报自己的状态, 当出现不上报或者上报超时, NameServer就会当这个节点故障了, 然后从列表中移除. 当新加节点上报状态后, NameServer就会把它加入节点列表. 那么Producer和Consumer就能正常的连接整个集群了

NameServer部署、相互独立

可以部署多个NameServer, 但都是相互独立的, 不是一个集群的概念. 主要考虑是实现热备份的目的

为什么不用zookeeper?

RocketMQ在2.x的时候使用的是zookeeper, 从3.x之后就不在使用了

因为zookeeper比较强大, 而RocketMQ对于zookeeper的功能很多都用不上

所以从3.x开始使用了量身定制的NameServer来替代zookeeper

NameServer的代码量非常少, 就一两千行, 就是一个RPC通讯, 再做几个MAP的维护, 由于代码少所以更易于维护

NameServer维护的主要信息:

org.apache.rocketmq.namesrv.routeinfo.RouteInfoManager

// key是topic名称, 存储着topic的信息, list的长度是这个topic数据存储的Master Broker的个数, QueueData存的是master broker的queue数量标识等
private final HashMap<String/* topic */, List<QueueData>> topicQueueTable;
// key是broker名称, 同一个brokerName会有多个节点, 一个主一个从. BrokerData存的就是节点的信息, ip地址等
private final HashMap<String/* brokerName */, BrokerData> brokerAddrTable;
// key是cluster(集群)名称, 存放该集群下的brokerName
private final HashMap<String/* clusterName */, Set<String/* brokerName */>> clusterAddrTable;
// key是broker地址, 只放可用的broker, 存放该broker的状态信息, 用于判断该Broker是否可用, 如不可用会删掉.
private final HashMap<String/* brokerAddr */, BrokerliveInfo> brokerLiveTable;
// key是broker地址, 存放服务的过滤器
private final HashMap<String/* brokerAddr */, list<String>/* Filter Server */> fi1terServerTable;// 4.4版本后不再支持