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