RocketMQ八股文
为什么要用MQ?
- 应用解耦
假设有支付、订单、短信三个服务,当有一笔订单需要调用这三个服务时,需要在代码中写死这三个服务的调用接口,如果需要新增一个新的服务积分服务,又要在代码中新增调用接口,耦合度太高,不利于管理
此时引入RocketMQ,通过发布不同的topic,需要的服务只需要订阅它就可以了,这样一来,生产者不需要知道我需要调用哪些接口,只管发送消息即可。未来再需要新增服务的时候,只需要在这个服务中订阅消息就可以了。扩展性提高,维护成本降低
- 异步提速
假设生产者向RocketMQ中发送一条用户下单的消息需要 50ms,生产者向数据库中发送一条消息需要 50ms,支付、订单、短信三个服务接收消息后处理需要 200ms
那么用户得到反馈的时间是多少呢? 50ms + 50ms = 100ms ,前端只需要将消息发送成功,数据库消息写入成功后就可以返回了,后端服务异步处理消息就行了,减少用户等待的时间
- 削峰填谷
在订单高峰期可能达到 10万/s 的qps,使用RocketMQ可以将这些消息存入消息队列中,再通过后端服务依次处理,将高峰期挤压的消息按照匀给其他时间处理,可以提高系统稳定性

Rocket-MQ的优点
- 开发语言:Rocket-MQ使用Java开发,更容易上手阅读体验和受众
- 社区氛围活跃:RocketMQ是阿里巴巴开源且内部在大量使用的消息队列,说明RocketMQ是经得起考验的,能够针对线上复杂环境的需求场景提供相应的解决方案
- 特性丰富:根据RocketMQ官方文档的举例,其高级特性达到了12种,比如顺序消息、事务消息、定时消息等;能够在复杂的业务下尽可能多的提供思路及解决方案
RocketMQ组件

生产者、Topic、消费者之间的关系

- 生产者:消息的发送方,负责指定主题向MQ发送消息
生产者如何将消息发送给broker?生产者需要指定topic,和NameServer建立一个tcp长连接,拉取最新的路由信息;通过路由信息找到自己要投递信息的topic分布在哪几台broker上,根据负载均衡向broker发送信息
- 消费者:消息的接收方,从主题订阅消息并消费
消费者组是一个具有相同消费逻辑的集合,一个主题可以由多个消费者组进行订阅和消费,作用是实现负载均衡和容错能力
拉取消息时,消费者可能拉主也可能拉从,根据负载决定
- Broker:MQ的中间件,负责存储和转发
一个Broker中可以有多个topic
- NameServer:管理维护
管理
- 生产者、Broker、消费者都需要向NameServer进行注册
- 生产者通过NameServer知道,自己的topic需要发送到哪一个Broker中
- 消费者通过NameServer知道,从哪一个Broker中订阅Topic消息
心跳机制
- 生产者、Broker、消费者每隔30s 或 1min就会向NameServer发送一次心跳
- 如果超出时间没有发送,该节点就会被移除,不再参与MQ的任何事件
- 当心跳恢复后,NameServer会将该节点加入MQ中参与事件
- messageQueue:消息的存储单元
- 唯一标识:每个队列有唯一标识,由topic名称和队列编号组成
- 消息顺序性:FIFO
- 负载均衡:动态调整消息分配,消息分配到不同队列中
消息模型
消息发送模式
- 同步发送
生产者发送消息后会阻塞当前线程,等待Broker返回结果,只有收到ack确认,才表示消息发送成功
优点:可靠性高;缺点:吞吐量低
使用场景:交易信息,核心订单创建,保证消息发送成功
- 异步发送
生产者发送消息后,立即返回,不会阻塞,Broker返回结果时会异步回调用户提供的回调函数
优点:吞吐量高;缺点:需要实现回调接口,消息无法保证成功
使用场景:能接受消息丢失,关注速度
- 单向发送
生产者发送消息后,立即返回,不关心发送结果
优点:吞吐量高;缺点:不知道消息是否发送成功,可靠性低
使用场景:日志收集
消费模式
- 消费方式
pull:消费者不断检查消息是否发送,如果有了就将消息拉回来
push:消费者提供一个监视器来监视消费者消费的topic,当生产者发送消息到Broker,会立即将该消息推送到消费者
优缺点
- pull拉:完全掌握数量和速度,避免消息堆积;但不断进行轮询,对中间件有一定压力
- push推:消息是实时的;但上传速度 > 消费速度,会造成消息堆积
- 推适合实时性高的场景;拉适合实时性不高的场景
- 消息分发
集群:一条消息只能被同一个消费者组中任意一个消费者消费。通过负载均衡可以分配给不同的消费者消费
- 目的:实现水平扩展和负载均衡,保证消息不会被重复消费
- 场景:绝大部分解耦,微服务之间的异步通信
广播:一条消息只能被同一个消费者组消费一次
- 目标:将消息通知给所有订阅者
- 场景:全局处理,更新全局配置
消费机制
- 顺序消费
场景:需要保证创建订单 -> 支付订单 -> 发货 -> 确认收货,这些消息必须按顺序处理
实现
- 全局顺序消费:我们知道一个topic可以有多个messageQueue,以提高系统的吞吐量;为了保证全局顺序消费,可以将队列数量设置为1,这样下单场景下各个消息一定会按照发送的顺序执行;但是这样设置极大降低了系统的吞吐量,不符合MQ的设计初衷
- 局部顺序消费:然后我们又引入了局部顺序消费的概念,第一要保证消息顺序发送,对应到MQ中就需要使用同步发送,异步发送无法保证顺序性;第二要保证消息顺序存储,MQ的Topic下会存在多个messageQueue,要保证消息顺序存储,同一个业务编号信息需要存储在一个MessageQueue中,这里可以使用messageQueueSelector来选择需要发送的messageQueue,原理就是对业务编号进行hash,根据队列数量对hash取余,将消息发送到一个messageQueue中;第三要保证消息顺序消费,由于前两步已经保证了消息顺序存储了,在这里只需要考虑顺序处理,即同一时刻,一个messageQueue中的消息只能被一个消费者中的一个消费线程消费
- 延迟消费
消息发送给Broker后不会立即处理,而是经过一个等待时间再发送给消费者,消费者是无感的
场景:每天5点执行文件清理,每隔2分钟触发一次消息推送等需求
实现:生产者设置message.setDelayTimeLevel(3)指定延迟级别为3; 之后Broker会将消息转移到延迟的Topic中,根据延迟级别存入特定队列,时间到了才会投递到目标Topic中
- 批量消费
消费者一次拉取处理多条消息,性能提升很大,但如果某条消息处理失败,需要整批消息一起重试
场景:日志收集和处理,数据同步迁移
实现:生产者使用 producer.send(Collection
- 事务消息
把 RocketMQ 里的“发消息”这件事,和你本地数据库事务绑在一起,要么都成功,要么都失败,避免出现“DB 成功了,但消息没发出去 / 多发一次”的操作。
实现:Producer先向Broker发送一条“半消息”,这条消息暂时对消费者不可见。半消息发送成功后,Producer开始执行本地事务,如果本地事务提交成功,调用commit,将“半消息”设置为消费者可见的,发送出去;如果本地事务提交失败,调用rollback,Broker将“半消息”删除,不会发送出去。如果由于网络波动问题,导致Broker没有收到commit/rollback,Broker会定时发起事务回查,问Producer是什么情况。
延迟消费

指数退避
指数退避是一种重试策略,每次重试失败后等待时间按指数级增长,比如 1s、2s、4s、8s、16s,而不是固定间隔反复重试。
这样可以在对端服务异常或高负载时,避免大量请求短时间内反复冲击,降低雪崩风险。实际工程里一般还会在基础的指数退避上增加随机抖动,把不同客户端的重试时间打散





