1. RocketMQ 是什么?
RocketMQ 是一个消息队列中间件。
它的核心作用是:
让一个系统把消息发出去,另一个系统异步接收并处理。
比如:
text
订单系统 ---> RocketMQ ---> 库存系统
订单系统 ---> RocketMQ ---> 积分系统
订单系统 ---> RocketMQ ---> 短信系统
订单系统只负责说:
"用户下单了。"
然后把这条消息发到 RocketMQ。
库存系统看到这个消息,就扣库存。
积分系统看到这个消息,就加积分。
短信系统看到这个消息,就发短信。
这样订单系统就不需要自己一个个调用它们。
2. 为什么要用 RocketMQ?
主要解决这几个问题。
1)异步处理,提高速度
不用消息队列时:
text
下单 -> 扣库存 -> 加积分 -> 发短信 -> 返回成功
用户要等所有步骤完成。
用了 RocketMQ:
text
下单 -> 发送消息 -> 返回成功
扣库存、加积分、发短信后面慢慢处理。
这样接口响应更快。
2)系统解耦
不用 MQ 时,订单系统可能要直接调用库存系统、短信系统、积分系统。
text
订单系统强依赖库存系统
订单系统强依赖短信系统
订单系统强依赖积分系统
如果短信系统挂了,订单系统可能也受影响。
用了 MQ:
text
订单系统只管发消息
其他系统自己订阅消息
订单系统不用关心谁来处理。
3)削峰填谷
比如秒杀场景,一瞬间来了 10 万个请求。
如果这些请求直接打到数据库,数据库可能崩。
可以先把请求写入 MQ:
text
大量请求 -> RocketMQ -> 后台慢慢消费 -> 数据库
RocketMQ 相当于一个缓冲池。
3. RocketMQ 里的核心概念
你先记住这些:
text
Producer:生产者,发消息的人
Consumer:消费者,收消息的人
Topic:消息主题,消息分类
Group:组,表示一类生产者或消费者
Broker:消息服务器,真正存消息的地方
NameServer:注册中心,帮客户端找到 Broker
Message:消息本身
Tag:Topic 下面的二级分类
Queue:Topic 内部的队列分片
下面一个个讲。
4. Topic 是什么?
Topic 是消息的分类。
比如你有这些业务:
text
订单消息
支付消息
优惠券消息
库存消息
你可以设计成:
text
order_topic
pay_topic
coupon_topic
stock_topic
Topic 就像一个"频道"。
生产者发送消息时,要指定发到哪个 Topic。
消费者订阅消息时,也要指定监听哪个 Topic。
例如:
text
订单系统发送消息到 order_topic
库存系统监听 order_topic
积分系统监听 order_topic
短信系统监听 order_topic
只要有系统对订单消息感兴趣,就订阅 order_topic。
Topic 的简单理解
你可以这样记:
text
Topic = 消息属于哪一类
比如:
text
下单成功消息 -> order_topic
支付成功消息 -> pay_topic
优惠券过期消息 -> coupon_topic
5. Group 是什么?
Group 分两种:
text
Producer Group
Consumer Group
但实际开发里,你最需要理解的是 Consumer Group。
5.1 Producer Group 是什么?
Producer Group 是生产者组。
它表示一类发送消息的生产者。
比如订单服务有多个实例:
text
order-service-1
order-service-2
order-service-3
它们都负责发送订单消息,可以属于同一个 Producer Group:
text
order_producer_group
小白阶段不用太纠结 Producer Group,它更多是标识生产者身份。
5.2 Consumer Group 是什么?
Consumer Group 是消费者组。
它非常重要。
它表示:
一组消费者共同消费同一类消息。
比如库存服务部署了 3 台机器:
text
stock-service-1
stock-service-2
stock-service-3
它们都属于:
text
stock_consumer_group
那么一条消息只会被这个组里的其中一个实例消费。
也就是说:
text
order_topic 有一条消息:用户A下单了
stock-service-1 消费
stock-service-2 不消费
stock-service-3 不消费
这样可以分摊压力。
6. 一个 Topic 可以被多个 Group 消费吗?
可以,而且这是 RocketMQ 很重要的地方。
假设订单系统发了一条消息:
text
Topic: order_topic
消息内容: 用户A下单成功
现在有三个系统都想处理这条消息:
text
库存系统:扣库存
积分系统:加积分
短信系统:发短信
它们应该用不同的 Consumer Group:
text
stock_consumer_group
point_consumer_group
sms_consumer_group
这样这条消息会被每个 Group 都消费一次。
也就是:
text
stock_consumer_group 消费一次
point_consumer_group 消费一次
sms_consumer_group 消费一次
但是在同一个 Group 内部,只会有一个消费者实例消费这条消息。
7. Topic 和 Group 的关系
这是最容易混乱的地方。
你可以这样理解:
text
Topic:消息分类
Group:谁来消费这一类消息
举个例子:
text
Topic: order_topic
Consumer Group:
- stock_group:库存系统消费
- sms_group:短信系统消费
- point_group:积分系统消费
关系图:
text
订单系统
|
| 发送订单消息
v
order_topic
|
|---- stock_group -> 扣库存
|
|---- sms_group -> 发短信
|
|---- point_group -> 加积分
所以:
text
同一个 Topic 可以被多个 Group 订阅。
不同 Group 都能拿到同一份消息。
同一个 Group 内部多个消费者是竞争消费。
8. Consumer Group 最重要的规则
假设:
text
Topic = order_topic
Consumer Group = stock_group
如果你启动了 3 个库存服务实例:
text
stock-service-1
stock-service-2
stock-service-3
它们都用同一个 stock_group。
那么消息会被负载均衡:
text
消息1 -> stock-service-1
消息2 -> stock-service-2
消息3 -> stock-service-3
消息4 -> stock-service-1
这叫集群消费。
默认常用的就是这种。
如果你想让每个实例都收到同一条消息,那叫广播消费。
比如:
text
消息1 -> stock-service-1
消息1 -> stock-service-2
消息1 -> stock-service-3
但业务开发中更常用的是集群消费。
9. Tag 是什么?
Tag 是 Topic 下面更细的分类。
比如你有一个 Topic:
text
order_topic
但是订单消息里面又分:
text
下单成功
订单取消
订单超时
订单完成
可以用 Tag:
text
order_topic: create
order_topic: cancel
order_topic: timeout
order_topic: finish
消费者可以只订阅自己关心的 Tag。
比如库存系统只关心:
text
order_topic 下的 create 和 cancel
短信系统只关心:
text
order_topic 下的 create
简单记:
text
Topic = 大分类
Tag = 小分类
10. Message 是什么?
Message 就是消息本身。
一般包括:
text
Topic
Tag
Key
Body
例如:
json
{
"topic": "order_topic",
"tag": "create",
"key": "order_10001",
"body": {
"orderId": 10001,
"userId": 20001,
"amount": 99.9
}
}
其中:
text
Topic:发到哪个主题
Tag:消息小分类
Key:消息唯一标识,方便排查
Body:真正的业务数据
11. Broker 是什么?
Broker 是 RocketMQ 里真正存消息的服务器。
生产者发送消息,实际上是发给 Broker。
消费者消费消息,也是从 Broker 拉消息。
text
Producer -> Broker -> Consumer
如果说 RocketMQ 是快递系统,那么 Broker 就是快递仓库。
12. NameServer 是什么?
NameServer 类似注册中心。
它的作用是:
告诉 Producer 和 Consumer,Broker 在哪里。
因为生产者和消费者并不知道 Broker 地址,所以它们先连接 NameServer。
text
Producer -> NameServer:请问 order_topic 在哪个 Broker?
NameServer -> Producer:在 Broker-A
Producer -> Broker-A:发送消息
你可以把 NameServer 理解成"地址簿"。
13. Queue 是什么?
一个 Topic 内部会分成多个 Queue。
比如:
text
order_topic
- queue0
- queue1
- queue2
- queue3
为什么要分多个 Queue?
为了提高并发能力。
如果只有一个队列,一个消费者慢慢消费,速度有限。
多个 Queue 可以让多个消费者并行消费。
text
queue0 -> consumer1
queue1 -> consumer2
queue2 -> consumer3
queue3 -> consumer4
小白阶段你不用主动操作 Queue,知道 Topic 内部有多个队列就行。
14. RocketMQ 的整体结构
完整流程大概是:
text
NameServer:负责发现 Broker
Broker:负责存消息
Producer:发送消息
Consumer:消费消息
Topic:消息分类
Group:生产者/消费者分组
整体图:
text
NameServer
^
|
查询 Broker 地址
|
Producer -----> Broker -----> Consumer
| | |
发送消息 存消息 拉取消息
|
Topic + Tag + Body
15. 开发中怎么设计 Topic 和 Group?
假设你做一个优惠券系统。
业务有:
text
优惠券创建
优惠券领取
优惠券使用
优惠券过期
可以设计 Topic:
text
coupon_topic
然后用不同 Tag:
text
create
receive
use
expire
比如优惠券过期消息:
text
Topic: coupon_topic
Tag: expire
Body: {"couponId": 1001}
消费者:
text
coupon_expire_consumer_group
它监听:
text
coupon_topic:expire
到时间后消费消息,把优惠券状态改成已过期。
16. RocketMQ 如何引入?以 Spring Boot 为例
常见方式是引入 RocketMQ Spring Boot Starter。
Maven 依赖一般类似这样:
xml
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.3.0</version>
</dependency>
版本要根据你的 Spring Boot 和 RocketMQ 服务端版本调整。
17. 配置 application.yml
比如:
yaml
rocketmq:
name-server: 127.0.0.1:9876
producer:
group: order_producer_group
这里:
text
name-server:NameServer 地址
producer.group:生产者组名
18. 生产者怎么发消息?
在 Spring Boot 里,可以用 RocketMQTemplate。
java
@RestController
@RequestMapping("/order")
public class OrderController {
private final RocketMQTemplate rocketMQTemplate;
public OrderController(RocketMQTemplate rocketMQTemplate) {
this.rocketMQTemplate = rocketMQTemplate;
}
@PostMapping("/create")
public String createOrder() {
// 1. 模拟订单创建成功
String orderId = "10001";
// 2. 发送订单创建消息
rocketMQTemplate.convertAndSend(
"order_topic:create",
"订单创建成功,订单ID:" + orderId
);
return "下单成功";
}
}
这里:
java
"order_topic:create"
表示:
text
Topic = order_topic
Tag = create
19. 消费者怎么接收消息?
写一个监听器:
java
@Component
@RocketMQMessageListener(
topic = "order_topic",
consumerGroup = "stock_consumer_group",
selectorExpression = "create"
)
public class StockConsumer implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
System.out.println("库存系统收到订单消息:" + message);
// 这里可以写扣库存逻辑
}
}
解释一下:
java
topic = "order_topic"
表示监听订单主题。
java
consumerGroup = "stock_consumer_group"
表示这个消费者属于库存消费者组。
java
selectorExpression = "create"
表示只消费 Tag 为 create 的消息。
20. 一个完整小例子
订单系统发消息
java
rocketMQTemplate.convertAndSend(
"order_topic:create",
"用户下单成功,订单ID:10001"
);
库存系统消费消息
java
@RocketMQMessageListener(
topic = "order_topic",
consumerGroup = "stock_consumer_group",
selectorExpression = "create"
)
public class StockConsumer implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
System.out.println("扣库存:" + message);
}
}
短信系统消费消息
java
@RocketMQMessageListener(
topic = "order_topic",
consumerGroup = "sms_consumer_group",
selectorExpression = "create"
)
public class SmsConsumer implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
System.out.println("发短信:" + message);
}
}
注意,这两个消费者用了不同的 Consumer Group:
text
stock_consumer_group
sms_consumer_group
所以同一条订单消息,库存系统和短信系统都能收到。
21. 如果两个消费者用了同一个 Group 会怎样?
比如:
java
consumerGroup = "order_consumer_group"
两个消费者都用这个组。
那么同一条消息只会被其中一个消费者收到。
text
消息1 -> 消费者A
消息2 -> 消费者B
消息3 -> 消费者A
这叫负载均衡。
所以千万记住:
text
不同业务系统,要用不同 Consumer Group。
同一个业务系统的多个实例,用同一个 Consumer Group。
这是面试和实战都很重要的一句话。
22. RocketMQ 有哪些常见消息类型?
1)普通消息
最常用。
text
订单创建后,发送一条订单消息
2)延迟消息
消息不是立刻消费,而是过一段时间再消费。
比如优惠券过期:
text
优惠券 20 小时后过期
可以发送延迟消息:
text
20 小时后消费者才收到消息
然后消费者把优惠券状态改为过期。
这就是你之前说的优惠券过期方案里可能会用到的方式。
3)顺序消息
保证消息按顺序消费。
比如订单状态变化:
text
创建订单 -> 支付订单 -> 发货 -> 完成
这些消息最好按照顺序消费。
4)事务消息
解决"本地事务"和"发消息"一致性问题。
比如:
text
订单创建成功后,必须发消息通知库存系统
如果订单创建成功了,但消息发送失败,就会出问题。
事务消息就是用来保证这类场景的一致性。
23. RocketMQ 使用中的几个重要问题
1)消息会不会重复消费?
会。
RocketMQ 不能保证业务层面绝对只消费一次。
所以消费者代码要做幂等。
幂等是什么意思?
就是同一条消息消费多次,结果也一样。
比如扣库存时,不能简单写:
text
库存 = 库存 - 1
否则重复消费就会多扣。
应该记录这条订单是否已经扣过库存:
text
如果订单10001已经扣过库存,就不要重复扣
2)消息消费失败怎么办?
如果消费者抛异常,RocketMQ 会重试。
比如:
java
@Override
public void onMessage(String message) {
throw new RuntimeException("消费失败");
}
RocketMQ 会认为这条消息没有消费成功,后面会重新投递。
所以你的消费逻辑要注意:
text
成功就正常返回
失败就抛异常
3)消息堆积是什么?
如果生产者发消息太快,消费者处理太慢,消息就会越积越多。
这叫消息堆积。
比如:
text
每秒产生 10000 条消息
每秒只能消费 1000 条消息
剩下的消息就会堆积在 Broker。
解决方式:
text
增加消费者实例
优化消费逻辑
增加 Topic 队列数
拆分业务 Topic
24. 面试怎么回答 Topic 和 Group?
你可以这样说:
Topic 是 RocketMQ 中消息的一级分类,生产者发送消息时需要指定 Topic,消费者通过订阅 Topic 来消费消息。
Group 分为 Producer Group 和 Consumer Group。实际开发中更常用的是 Consumer Group,它表示一组消费者实例。
同一个 Consumer Group 内部是竞争消费,一条消息只会被组内一个消费者消费;不同 Consumer Group 之间是独立消费,同一条消息可以被多个不同业务组分别消费。
所以一般不同业务系统使用不同 Consumer Group,同一业务系统的多个实例使用同一个 Consumer Group。
这段可以背下来。
25. 小白最容易记混的点
Topic 不是消费者
Topic 只是消息分类。
text
order_topic 不是订单服务
order_topic 是订单消息的分类
Group 不是 Topic 的子分类
Group 表示消费者身份,不是消息分类。
text
Topic = 消息是什么
Group = 谁来消费
Tag 才是 Topic 下面的小分类
text
Topic = order_topic
Tag = create / cancel / pay / finish
26. 你可以这样记
text
Producer:发消息的人
Consumer:收消息的人
Topic:消息大分类
Tag:消息小分类
Group:一组生产者或消费者
Broker:存消息的服务器
NameServer:帮你找 Broker 的地址簿
Queue:Topic 内部的并发队列
Message:真正传递的数据
27. 最后用一句话串起来
比如订单创建成功:
text
订单服务作为 Producer,
把一条 order_topic:create 消息发送到 Broker,
Broker 负责保存消息,
库存服务、短信服务、积分服务作为不同 Consumer Group,
分别订阅 order_topic,
收到消息后执行扣库存、发短信、加积分等逻辑。
NameServer 负责告诉生产者和消费者 Broker 在哪里。
你现在先重点记这三个关系就够了:
text
Topic:消息分类
Tag:Topic 下的小分类
Consumer Group:消费者分组
以及这句:
text
同一个 Group 内部,一条消息只给一个消费者;
不同 Group 之间,同一条消息可以各消费一遍。
这就是 RocketMQ 入门里最核心的东西。