rocketmq-消息路由,消息存储,消息消费

26 阅读3分钟

消息路由

消息发送,最主要是消息路由,所谓路由,就是到底写到哪个节点,因为都是集群部署。

核心类是MessageQueue。

包含了队列id字段。源码:

/**
 * 作用:消息队列,主要作用是路由,不是逻辑存储,也不存储消息
 *
 * 只和发送者有关系,和消费者没有关系
 *
 * @author gzh
 */
public class MessageQueue implements Comparable<MessageQueue>, Serializable {
    private static final long serialVersionUID = 6191200464116433425L;
    private String topic; //topic名字
    private String brokerName; //存储节点名字。生产者可以根据名字找到存储节点。
    private int queueId; //消息队列id。brokerName是哪个节点,queueId是哪个队列。

其实就是这个类决定了写消息到哪个节点的哪个队列。

一般情况下的负载均衡是只需要决定写到哪个节点即可,但是rocketmq单个broker节点默认包含4个队列,既然有多个队列,那么写消息的时候,就要决定到底写到该节点的哪个队列。


从源码可以看出来,这个只是路由,起一个标记的作用,而不是存储,存储是CommitLog。


消息路由MessageQueue和消费者没有关系。

消息存储

核心类是CommitLog。

说白了,就是物理存储,就是一个物理文件。

写消息的时候,最终写到这个物理文件。

它的目录结构是:

store/CommitLog/文件1(MapFile)

如果数据比较少,其实一个文件就够了,但是实际是数据比较多,所以每个文件是1g,满了就创建新的文件。

虽然有多个文件,但是多个文件可以看成只有一个文件,因为是按顺序写入的,只有上一个文件满了,才会创建下一个文件,本质还是同一个存储文件。


单个broker节点,只有一个CommitLog文件。不像队列,是有多个。


小结

CommitLog是物理存储文件:
1、单个broker节点,只有一个CommitLog,所以是串行执行。
2、每个子文件1G,如果满了,就创建下一个子文件。每个子文件是一个MappedFile。


实际目录结构举例

image.png

juejin.cn/post/713026…

消息消费

核心类是ConsumeQueue。

主要作用是用于消费者消费,ConsumeQueue和生产者没有关系。

相当于是逻辑队列,逻辑存储,因为物理存储是CommitLog。

虽然是逻辑队列,但是也是文件。就像索引也是文件,虽然不存储表数据。


目录结构:

$HOME/store/consumequeue/{topic}/{queueId}/{fileName}

举例:单个broker默认有4个队列,所以queueId为0、1、2、3,topic为topicA,那么consumequeue文件夹的组织结构如下:$HOME/store/consumequeue/topicA/0/00000000000000000000。

fileName也是有多个文件组成,每个文件的大小为20M。


源码

其实和MessageQueue类似,也是包含了几个核心字段:
1.哪个节点
2.节点上的哪个队列
3.哪个topic

只不过呢,它更复杂,除此之外,还包含了记录消息消费进度的功能。

rocketmq是基于数组还是链表实现队列?

rocketmq并没有什么队列的数据结构,也不是基于数组或者链表去实现队列。

那它怎么实现队列功能呢?就是基于上文的几个核心类,组合实现队列功能。

物理存储就是按顺序写入CommitLog物理文件。内存里面并没有队列这个数据结构,也没有相关的队列类,或者封装了队列的类。