队列(MessageQueue)是Apache RocketMQ中消息存储和传输的实际容器,也是Apache RocketMQ消息的最小存储单元。在Apache RocketMQ的所有主题中,由多个队列组成,以实现队列数量的水平拆分和队列内部的流式存储。
队列的主要作用如下:
1. 存储顺序性:队列天然具备顺序性,即消息按照进入队列的顺序写入存储。同一队列间的消息存在顺序关系,队列头部为最早写入的消息,队列尾部为最新写入的消息。消息在队列中的位置和消息之间的顺序通过位点(Offset)进行标记管理。
2. 流式操作语义:基于队列的存储模型可确保消息从任意位点读取任意数量的消息,实现类似聚合读取、回溯读取等特性。这些特性是RabbitMQ、ActiveMQ等非队列存储模型不具备的。
在整个Apache RocketMQ的领域模型中,队列所处的流程和位置如下:
1. Apache RocketMQ默认提供消息可靠存储机制,所有发送成功的消息都被持久化存储到队列中,配合生产者和消费者客户端的调用可实现至少投递一次的可靠性语义。
2. Apache RocketMQ队列模型与Kafka的分区(Partition)模型类似。在Apache RocketMQ消息收发模型中,队列属于主题的一部分。虽然所有的消息资源以主题粒度管理,但实际的操作实现是面向队列。例如,生产者指定某个主题,向主题内发送消息,但实际消息发送到该主题下的某个队列中。
3. 通过修改队列数量,可以实现横向的水平扩容和缩容。
队列的内部属性包括读写权限、行为约束等:
- 读写权限:定义了当前队列是否可以读写数据。取值有0(不可读写状态)、4(只读状态)、6(读写状态)和2(只写状态)。注意,队列的读写权限属于运维侧操作,不建议频繁修改。
- 行为约束:每个主题下会由一到多个队列来存储消息,每个主题对应的队列数与消息类型以及实例所处地域(Region)相关。
版本兼容性:Apache RocketMQ 服务端的不同版本对队列名称属性有以下差异。在服务端3.x/4.x版本中,队列名称由主题名称、BrokerID和QueueID三元组组成,并与物理节点绑定。而在服务端5.x版本中,队列名称为一个集群分配的全局唯一的字符串,与物理节点解耦。因此,开发过程中不建议对队列名称做任何假设和绑定。如果在代码中自定义拼接队列名称并与其他操作进行绑定,一旦服务端版本升级,可能会出现队列名称无法解析的兼容性问题。
使用建议:根据实际业务消耗设置队列数。Apache RocketMQ 的队列数可在创建主题或变更主题时设置修改。队列数量的设置应遵循少用够用原则,避免随意增加队列数量。过多的主题内队列可能导致以下问题:
- 集群元数据膨胀:Apache RocketMQ 会以队列粒度采集指标和监控数据,过多的队列容易造成管控元数据膨胀。
- 客户端压力过大:Apache RocketMQ 的消息读写都是针对队列进行操作,过多的队列对应更多的轮询请求,增加系统负荷。
常见队列增加场景包括:需要增加队列实现物理节点负载均衡;需要增加队列实现顺序消息性能扩展等。