Topic概念定义
- 主题:RocketMQ中消息传输和存储的顶层容器,用于标识同类业务中逻辑的消息,可理解为消息的分类,主题消息的分类取决于业务,要发送的业务消息最好单独是一个Topic主题,以保证互相不被干扰
- Topic主要有两大核心作用:
1)定义数据的分类隔离:将不同业务类型的数据拆分到不同的主题中管理,通过主题实现存储的隔离性和订阅隔离性
2)定义数据的身份和权限:RocketMQ的消息本身是匿名无身份的,同一分类的消息使用相同的主题来做身份识别和权限管理
模型关系
- 在RocketMQ中主题Topic所处的流程和位置如下:
- Topic是一个逻辑概念,而非实际的消息容器
- 每个Topic包含多个消息队列(Queue),队列是消息的实际存储单元,每个队列独立存储消息,队列数量在创建Topic时可以配置,也可后续动态调整
- 生产者发送消息时,消息会根据一定策略分配到不同的队列中,常见的分配策略包括轮询(Round Robin)和哈希(Hashing)
- 消费者消费时,同样是从这些队列中拉取消息
Topic属性和配置
- Topic主要有以下内部属性可用来配置:
主题名称
- 主题名称是在创建主题时,由开发者定义,主要用于标识主题,名称需要在集群内唯一,官方给名称命名的建议如下:
1)Topic命名应该尽量使用简短、常用的字符,字符过长可能会导致拒绝收发消息
2)避免使用特殊字符,特殊字符可能会出现解析异常
队列列表
- 队列既是主题的组成单元,又是消息的最小存储单元
- 队列需要在创建主题时指定,一个主题内至少包含一个队列,消息实际存储在主题的各队列中
队列的主要作用如下: - 存储顺序性:队列天然具备顺序性,消息按照进入队列的顺序进行读取,即先进先出,消息在队列中的位置和消息之间的顺序通过位点(Offset)来标记管理
- 流式操作语义:RocketMQ基于队列的存储模型可确保消息从任意Offset读取任意数量的消息,以此实现类似聚合读取,回溯读取等特性,这些特性是RabbitMQ、ActiveMQ等非队列存储模型所不具备的
消息类型
消息类型是RocketMQ主题所支持的消息类型,在创建主题时需要选择消息类型,主要包括以下消息类型:
1)Normal
- Normal:普通消息,是RocketMQ默认的消息类型,消息本身无特殊语义,消息之间也没有任何关系
- 普通消息没有复杂的处理逻辑,适用于大多数常见的消息传递场景,消息从生产者发送到Broker,然后再由消费者拉取并处理
// 初始化消息⽣产者
DefaultMQProducer producer = new
DefaultMQProducer("ProducerGroupName");
producer.setNamesrvAddr("127.0.0.1:9876");
// 启动⽣产者
producer.start();
try {
// 创建⼀个消息实例,指定主题、标签和消息体
Message msg = new Message("TopicTest", "TagA", ("Hello
RocketMQ").getBytes(RemotingHelper.DEFAULT_CHARSET));
// 发送消息到 Broker
SendResult sendResult = producer.send(msg);
// 打印发送结果
System.out.printf("%s%n", sendResult);
} catch (Exception e) {
e.printStackTrace();
}
// 关闭⽣产者
producer.shutdown();
2)FIFO
- 顺序消息:消息按照发送的顺序被严格地消费,适用于对消息顺序有严格要求的场景,比如:数据实时增量同步
- RocketMQ支持两种类型的顺序消息:全局顺序消息和分区顺序消息
- 全局顺序消息可保证同一个Topic下的所有消息都按照严格的顺序被消费,即所有消息按照被发送的顺序依次被消费,由于该消息类型被限制了并发量,所以使用较少
- 分区顺序消息可保证在某个分区(某个队列)中的消息会按照顺序消费,但不同分区间的消息消费顺序无法保证;分区顺序消费通过将消息分配到不同的队列,以此实现更高的并发性能
3)Delay
- 定时/延时消息,通过指定延时的时间控制消息生产后不要立即投递,而是在延迟一定时间后才对消费者可见;延迟消息适用于延迟任务、任务超时处理、分布式定时调度等场景
4)Transaction
- 事务消息:基于消息队列来实现分布式事务,保证事务最终一致性的较好解决方案
扩展
在选择消息类型时有些限制约束:
- RocketMQ从5.0开始,支持强制校验消息类型,即每个主题只允许发送一种类型的消息,以便于更好的管理生产系统;为保证向下兼容4.x版本,强制校验功能默认开启,即:
1)消息类型必须一致:发送消息的类型,必须和目标主题定义的消息类型一致
2)主题类型必须单一:每个主题只支持一种消息类型,不允许将多种类型的消息发送到同一个主题中
Topic常见问题和解决方案
Topic拆分过细
- Topic会耗费资源,拆分过细,会消耗大量主题资源,造成系统负载过重,比如:发送企微、钉钉、公众号消息都使用一个主题,这会系统带来很大的压力
Topic拆分过粗
- 如果拆分粒度过粗,甚至不拆分,就会导致业务隔离性差,不利于独立运维和处理故障,比如:MQ阻塞,排查问题就会比较麻烦
Topic管理线上开启自动化
- RocketMQ提供了自动创建主题的功能,如果线上忘记关闭该功能,一段时间后就会产生大量垃圾主题且无法进行管理和回收,造成系统资源浪费
- 主题属于顶层资源和容器,应该拥有独立的权限管理、可观测性指标采集和监控等能力,创建和管理主题会占用一定的系统资源,因此,生产环境需要严格管理主题资源,不能随意进行增上改查操作,也建议控制好权限,修改主题操作走审批和CR,防止事故的发生
总结
- Topic是RocketMQ中核心的模型概念,用于标识同一类业务逻辑的消息
- 拆分Topic不能过细,不能过粗,适中为宜
- 从以下角度考虑拆分粒度:
1)消息类型是否一致:不同类型的消息,如顺序消息和普通消息需要使用不同的主题
2)消息业务是否关联:如果业务没有直接关联,如:淘宝的交易消息和盒马物流的消息没有业务交集,需要使用不同的消息主题;同样是淘宝交易消息,女装类订单和男装类订单可以使用同一个订单;但如果业务量较大或其他子模块应用处理业务时需要进一步拆分订单类型,也可以将男装和女装订单的消息拆分到两个主题中
3)消息量级/时效性是否相同:数量级不同或时效性不同的业务消息建议使用不同的主题,如:某些业务消息量很小但时效性要求很强,如果和某些亿万级消息量的业务使用同一个主题,会增加消息的等待时长