RocketMQ 核心原理与实战
一、知识概述
Apache RocketMQ 是阿里巴巴开源的分布式消息中间件,经历了多年双十一流量洪峰的考验。RocketMQ 以高可靠、高可用、高性能著称,特别适合电商、金融等对消息可靠性要求极高的场景。
本文将深入讲解 RocketMQ 的架构设计、核心概念、消息模型、事务消息等特性,并通过实战代码演示 RocketMQ 的使用方法。
二、架构设计
2.1 整体架构
+-------------------+ +-------------------+ +-------------------+
| Producer 1 | | Producer 2 | | Producer 3 |
+--------+----------+ +--------+----------+ +--------+----------+
| | |
+-----------+-------------+-------------------------+
|
v
+----------------------------------------------------------------+
| NameServer Cluster |
| +-------------+ +-------------+ +-------------+ |
| | NameServer 1| | NameServer 2| | NameServer 3| |
| +-------------+ +-------------+ +-------------+ |
| (无状态,不通信,各自独立) |
+----------------------------------------------------------------+
|
+-----------+-------------+-------------------------+
| | |
v v v
+--------+----------+ +--------+----------+ +--------+----------+
| Broker 1 | | Broker 2 | | Broker 3 |
| +---------------+ | | +---------------+ | | +---------------+ |
| | Topic A-M | | | | Topic A-S | | | | Topic B-M | |
| | (Master) | | | | (Slave) | | | | (Master) | |
| +---------------+ | | +---------------+ | | +---------------+ |
| | Topic B-S | | | | Topic B-M | | | | Topic A-M | |
| | (Slave) | | | | (Master) | | | | (Slave) | |
| +---------------+ | | +---------------+ | | +---------------+ |
+-------------------+ +-------------------+ +-------------------+
| | |
+-----------+-------------+-------------------------+
|
+-----------+---------------+-------------------------+
| | |
v v v
+--------+----------+ +--------+----------+ +--------+----------+
| Consumer Group A | | Consumer Group B | | Consumer Group C |
| (Push Consumer) | | (Pull Consumer) | | (Push Consumer) |
+-------------------+ +-------------------+ +-------------------+
2.2 核心组件
NameServer(名字服务器)
类似注册中心,管理 Broker 路由信息。特点:
- 无状态设计,节点之间不通信
- Broker 定期向所有 NameServer 发送心跳
- Producer/Consumer 从 NameServer 获取路由信息
- 轻量级,部署简单
Broker(消息服务器)
消息存储和转发的核心节点:
- Master:主节点,处理读写请求
- Slave:从节点,同步 Master 数据,可处理读请求
- 通过 BrokerId 区分:0 表示 Master,非 0 表示 Slave
Producer(生产者)
发送消息的应用,支持:
- 同步发送
- 异步发送
- 单向发送(不等待响应)
- 事务消息
- 延迟消息
Consumer(消费者)
接收消息的应用,支持两种消费模式:
- Push Consumer:Broker 主动推送消息
- Pull Consumer:消费者主动拉取消息
2.3 与 Kafka 的对比
| 特性 | RocketMQ | Kafka |
|---|---|---|
| 设计目标 | 业务消息 | 日志/流处理 |
| 消息可靠性 | 极高(金融级别) | 高 |
| 事务消息 | 支持 | 不支持(需要额外实现) |
| 延迟消息 | 支持(18 级延迟) | 不支持 |
| 消息过滤 | 支持 Tag、SQL92 | 不支持 |
| 消息回溯 | 支持 | 支持 |
| 死信队列 | 支持 | 不支持 |
| 消息轨迹 | 支持 | 不支持 |
| 吞吐量 | 高(万级/秒) | 极高(十万级/秒) |
三、核心概念
3.1 Topic 与 Queue
Topic: ORDER_TOPIC
├── Queue 0 (Broker 1, Master) ──── Queue 0 (Broker 2, Slave)
├── Queue 1 (Broker 1, Master) ──── Queue 1 (Broker 2, Slave)
├── Queue 2 (Broker 3, Master) ──── Queue 2 (Broker 4, Slave)
└── Queue 3 (Broker 3, Master) ──── Queue 3 (Broker 4, Slave)
特点:
1. 一个 Topic 包含多个 Queue(分区)
2. Queue 是消息存储的最小单位
3. Queue 分布在不同 Broker 上实现负载均衡
4. Queue 数量决定最大并行消费者数量
3.2 消息模型
消息结构
Message message = new Message(
"ORDER_TOPIC", // Topic
"TagA", // Tag(可选,用于过滤)
"ORDER_001", // Keys(可选,用于查询)
"Order content".getBytes() // Body
);
// 消息属性
message.setDelayTimeLevel(3); // 延迟级别
message.setWaitStoreMsgOK(true); // 是否等待存储成功
message.putUserProperty("userId", "123"); // 自定义属性
消息类型
/**
* 1. 普通消息
*/
Message normalMsg = new Message("Topic", "Hello".getBytes());
/**
* 2. 顺序消息
* - 通过 MessageQueueSelector 指定队列
*/
Message orderMsg = new Message("Topic", "orderId", "Hello".getBytes());
/**
* 3. 延迟消息
* - 18 个延迟级别:
* 1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
*/
Message delayMsg = new Message("Topic", "Hello".getBytes());
delayMsg.setDelayTimeLevel(3); // 10秒延迟
/**
* 4. 事务消息
* - 保证本地事务与消息发送的原子性
*/
Message txMsg = new Message("Topic", "Hello".getBytes());
/**
* 5. 批量消息
*/
List<Message> messages = new ArrayList<>();
messages.add(new Message("Topic", "Msg1".getBytes()));
messages.add(new Message("Topic", "Msg2".getBytes()));
3.3 消费模式
集群消费(Clustering)
同一消费者组内,每条消息只会被一个消费者消费(默认模式)。
Topic: ORDER_TOPIC (4 Queues)
├── Queue 0 → Consumer 1
├── Queue 1 → Consumer 1
├── Queue 2 → Consumer 2
└── Queue 3 → Consumer 2
消费者组:ORDER_CONSUMER_GROUP
广播消费(Broadcasting)
每条消息会被消费者组内所有消费者消费。
Topic: ORDER_TOPIC
├── Queue 0 → Consumer 1, Consumer 2, Consumer 3
├── Queue 1 → Consumer 1, Consumer 2, Consumer 3
├── Queue 2 → Consumer 1, Consumer 2, Consumer 3
└── Queue 3 → Consumer 1, Consumer 2, Consumer 3
每个消费者都收到所有消息
四、生产者实战
4.1 生产者配置
/**
* 生产者配置
*/
public class ProducerConfig {
public static DefaultMQProducer createProducer(String groupName) {
DefaultMQProducer producer = new DefaultMQProducer(groupName);
// NameServer 地址
producer.setNamesrvAddr("localhost:9876");
// 发送消息超时时间
producer.setSendMsgTimeout(3000);
// 消息最大大小(默认 4MB)
producer.setMaxMessageSize(4 * 1024 * 1024);
// 重试次数
producer.setRetryTimesWhenSendFailed(3);
// 异步发送重试次数
producer.setRetryTimesWhenSendAsyncFailed(3);
// 是否在内部重试时选择其他 Broker
producer.setRetryAnotherBrokerWhenNotStoreOK(true);
// 压缩阈值(超过此大小压缩)
producer.setCompressMsgBodyOverHowmuch(4096);
return producer;
}
}
4.2 发送消息示例
/**
* 完整的生产者示例
*/
public class OrderProducer {
private final DefaultMQProducer producer;
public OrderProducer() throws MQClientException {
producer = new DefaultMQProducer("ORDER_PRODUCER_GROUP");
producer.setNamesrvAddr("localhost:9876");
producer.start();
}
/**
* 同步发送
*/
public SendResult sendSync(String topic, String tag, String key,
String message) throws Exception {
Message msg = new Message(topic, tag, key, message.getBytes());
SendResult result = producer.send(msg);
System.out.printf("发送结果: %s, MsgId=%s, QueueId=%d, Offset=%d%n",
result.getSendStatus(),
result.getMsgId(),
result.getMessageQueue().getQueueId(),
result.getQueueOffset());
return result;
}
/**
* 异步发送
*/
public void sendAsync(String topic, String tag, String key,
String message) {
Message msg = new Message(topic, tag, key, message.getBytes());
producer.send(msg, new SendCallback() {
@Override
public void onSuccess(SendResult result) {
System.out.println("异步发送成功: " + result.getMsgId());
}
@Override
public void onException(Throwable e) {
System.err.println("异步发送失败: " + e.getMessage());
// 可以在这里实现重试或补偿逻辑
}
});
}
/**
* 单向发送(不等待响应,可能丢失)
*/
public void sendOneway(String topic, String tag, String message) {
Message msg = new Message(topic, tag, message.getBytes());
producer.sendOneway(msg);
}
/**
* 发送延迟消息
*/
public SendResult sendDelayMessage(String topic, String message,
int delayLevel) throws Exception {
Message msg = new Message(topic, message.getBytes());
// 延迟级别:
// 1: 1s 2: 5s 3: 10s 4: 30s 5: 1m
// 6: 2m 7: 3m 8: 4m 9: 5m 10: 6m
// 11: 7m 12: 8m 13: 9m 14: 10m 15: 20m
// 16: 30m 17: 1h 18: 2h
msg.setDelayTimeLevel(delayLevel);
return producer.send(msg);
}
/**
* 发送顺序消息
*/
public SendResult sendOrderly(String topic, String message,
String orderId) throws Exception {
Message msg = new Message(topic, message.getBytes());
// 根据 orderId 选择队列,保证同一订单的消息发送到同一队列
SendResult result = producer.send(msg, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> mqs,
Message msg, Object arg) {
String id = (String) arg;
int index = Math.abs(id.hashCode()) % mqs.size();
return mqs.get(index);
}
}, orderId);
return result;
}
/**
* 发送批量消息
*/
public SendResult sendBatch(List<String> messages) throws Exception {
List<Message> msgList = new ArrayList<>();
for (String msg : messages) {
msgList.add(new Message("ORDER_TOPIC", msg.getBytes()));
}
// 批量发送要求:相同 Topic、相同等待存储结果
return producer.send(msgList);
}
/**
* 发送带属性的消息(用于 SQL 过滤)
*/
public SendResult sendWithProperty(String topic, String message,
Map<String, String> properties)
throws Exception {
Message msg = new Message(topic, message.getBytes());
for (Map.Entry<String, String> entry : properties.entrySet()) {
msg.putUserProperty(entry.getKey(), entry.getValue());
}
return producer.send(msg);
}
/**
* 发送事务消息
*/
public void sendTransactionMessage(String topic, String message,
Object transactionArg) {
Message msg = new Message(topic, message.getBytes());
// 事务监听器
TransactionListener transactionListener = new TransactionListener() {
// 执行本地事务
@Override
public LocalTransactionState executeLocalTransaction(Message msg,
Object arg) {
try {
// 执行本地业务逻辑
boolean success = executeLocalBusiness(arg);
if (success) {
return LocalTransactionState.COMMIT_MESSAGE;
} else {
return LocalTransactionState.ROLLBACK_MESSAGE;
}
} catch (Exception e) {
return LocalTransactionState.ROLLBACK_MESSAGE;
}
}
// 事务回查
@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
// 根据消息 Key 查询本地事务状态
String transactionId = msg.getTransactionId();
boolean committed = checkTransactionStatus(transactionId);
if (committed) {
return LocalTransactionState.COMMIT_MESSAGE;
} else {
return LocalTransactionState.ROLLBACK_MESSAGE;
}
}
};
// 创建事务生产者
TransactionMQProducer txProducer = new TransactionMQProducer(
"TX_PRODUCER_GROUP");
txProducer.setNamesrvAddr("localhost:9876");
txProducer.setTransactionListener(transactionListener);
// 设置事务回查线程池
txProducer.setCheckThreadPoolMinSize(2);
txProducer.setCheckThreadPoolMaxSize(5);
try {
txProducer.start();
// 发送事务消息(半消息)
TransactionSendResult result = txProducer.sendMessageInTransaction(
msg, transactionArg);
System.out.println("事务消息发送结果: " + result.getSendStatus());
} catch (Exception e) {
e.printStackTrace();
}
}
private boolean executeLocalBusiness(Object arg) {
// 执行本地业务
return true;
}
private boolean checkTransactionStatus(String transactionId) {
// 检查事务状态
return true;
}
public void shutdown() {
producer.shutdown();
}
// 使用示例
public static void main(String[] args) throws Exception {
OrderProducer producer = new OrderProducer();
// 同步发送
producer.sendSync("ORDER_TOPIC", "TagA", "KEY_001", "Order 001");
// 异步发送
producer.sendAsync("ORDER_TOPIC", "TagA", "KEY_002", "Order 002");
// 延迟消息
producer.sendDelayMessage("ORDER_TOPIC", "Delayed order", 5);
// 顺序消息
producer.sendOrderly("ORDER_TOPIC", "Order sequence", "ORDER_123");
// 批量发送
producer.sendBatch(Arrays.asList("Msg1", "Msg2", "Msg3"));
// 发送带属性的消息
Map<String, String> props = new HashMap<>();
props.put("userId", "12345");
props.put("region", "CN");
producer.sendWithProperty("ORDER_TOPIC", "Order with props", props);
Thread.sleep(5000); // 等待异步消息完成
producer.shutdown();
}
}
五、消费者实战
5.1 消费者配置
/**
* 消费者配置
*/
public class ConsumerConfig {
public static DefaultMQPushConsumer createPushConsumer(String groupName)
throws MQClientException {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(groupName);
// NameServer 地址
consumer.setNamesrvAddr("localhost:9876");
// 消费模式:集群(默认)或广播
consumer.setMessageModel(MessageModel.CLUSTERING);
// consumer.setMessageModel(MessageModel.BROADCASTING);
// 消费起点
// CONSUME_FROM_LAST_OFFSET:从最后的 offset 开始(默认)
// CONSUME_FROM_FIRST_OFFSET:从头开始
// CONSUME_FROM_TIMESTAMP:从指定时间开始
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
// 批量消费数量
consumer.setConsumeMessageBatchMaxSize(16);
// 最小消费线程数
consumer.setConsumeThreadMin(20);
// 最大消费线程数
consumer.setConsumeThreadMax(64);
// 拉取间隔时间(毫秒)
consumer.setPullInterval(0);
// 每次拉取最大数量
consumer.setPullBatchSize(32);
// 消息重试次数(-1 表示 16 次)
consumer.setMaxReconsumeTimes(16);
return consumer;
}
}
5.2 Push 消费者示例
/**
* Push 消费者(推荐)
*/
public class OrderPushConsumer {
private final DefaultMQPushConsumer consumer;
public OrderPushConsumer(String groupName) throws MQClientException {
consumer = new DefaultMQPushConsumer(groupName);
consumer.setNamesrvAddr("localhost:9876");
// 订阅 Topic(订阅所有 Tag)
consumer.subscribe("ORDER_TOPIC", "*");
// 订阅特定 Tag
// consumer.subscribe("ORDER_TOPIC", "TagA || TagB");
// 订阅并使用 SQL 过滤
// consumer.subscribe("ORDER_TOPIC",
// MessageSelector.bySql("userId = '12345' AND region = 'CN'"));
// 注册消息监听器
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(
List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
for (MessageExt msg : msgs) {
try {
String topic = msg.getTopic();
String tags = msg.getTags();
String keys = msg.getKeys();
String body = new String(msg.getBody());
System.out.printf(
"收到消息: Topic=%s, Tag=%s, Key=%s, Body=%s%n",
topic, tags, keys, body);
// 处理消息
processMessage(msg);
} catch (Exception e) {
System.err.println("处理消息失败: " + e.getMessage());
// 稍后重试
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
}
}
// 消费成功
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();
System.out.println("消费者启动成功");
}
private void processMessage(MessageExt msg) {
// 业务处理逻辑
}
public void shutdown() {
consumer.shutdown();
}
public static void main(String[] args) throws Exception {
OrderPushConsumer consumer = new OrderPushConsumer("ORDER_CONSUMER_GROUP");
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("正在关闭消费者...");
consumer.shutdown();
}));
Thread.sleep(Long.MAX_VALUE);
}
}
5.3 顺序消费者示例
/**
* 顺序消费者
*/
public class OrderlyConsumer {
public OrderlyConsumer(String groupName) throws MQClientException {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(groupName);
consumer.setNamesrvAddr("localhost:9876");
consumer.subscribe("ORDER_TOPIC", "*");
// 注册顺序消息监听器
consumer.registerMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(
List<MessageExt> msgs, ConsumeOrderlyContext context) {
// 设置自动提交
context.setAutoCommit(true);
for (MessageExt msg : msgs) {
try {
String orderId = msg.getKeys();
String body = new String(msg.getBody());
System.out.printf("顺序消费: OrderId=%s, QueueId=%d%n",
orderId, msg.getQueueId());
// 处理订单消息
processOrderMessage(orderId, body);
} catch (Exception e) {
// 顺序消费失败,可以挂起一段时间后重试
return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
}
}
return ConsumeOrderlyStatus.SUCCESS;
}
});
consumer.start();
}
private void processOrderMessage(String orderId, String body) {
// 按顺序处理订单
System.out.println("处理订单: " + orderId);
}
}
5.4 Pull 消费者示例
/**
* Pull 消费者(手动控制)
*/
public class OrderPullConsumer {
private final DefaultMQPullConsumer consumer;
private final Set<MessageQueue> mqs;
public OrderPullConsumer(String groupName) throws MQClientException {
consumer = new DefaultMQPullConsumer(groupName);
consumer.setNamesrvAddr("localhost:9876");
consumer.start();
// 获取 Topic 的所有队列
mqs = consumer.fetchSubscribeMessageQueues("ORDER_TOPIC");
}
public void pullMessages() {
// 为每个队列启动一个拉取线程
for (MessageQueue mq : mqs) {
new Thread(() -> pullFromQueue(mq)).start();
}
}
private void pullFromQueue(MessageQueue mq) {
long offset = 0;
while (true) {
try {
// 拉取消息
PullResult result = consumer.pull(
mq, // 消息队列
"*", // Tag 过滤
offset, // 起始 offset
32 // 最大拉取数量
);
if (result.getPullStatus() == PullStatus.FOUND) {
List<MessageExt> msgs = result.getMsgFoundList();
for (MessageExt msg : msgs) {
processMessage(msg);
offset = msg.getQueueOffset() + 1;
}
// 存储 offset
consumer.updateConsumeOffset(mq, offset);
} else if (result.getPullStatus() == PullStatus.NO_NEW_MSG) {
// 没有新消息,等待一会儿
Thread.sleep(1000);
} else if (result.getPullStatus() == PullStatus.OFFSET_ILLEGAL) {
// offset 非法,重置
offset = consumer.minOffset(mq);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void processMessage(MessageExt msg) {
System.out.printf("拉取到消息: %s%n", new String(msg.getBody()));
}
public void shutdown() {
consumer.shutdown();
}
}
六、高级特性
6.1 事务消息
/**
* 事务消息完整示例
*
* 应用场景:保证本地事务和消息发送的原子性
* 例如:下单后发消息通知库存扣减
*/
public class TransactionMessageDemo {
public static void main(String[] args) throws Exception {
// 创建事务监听器
TransactionListener transactionListener = new TransactionListener() {
// 执行本地事务
@Override
public LocalTransactionState executeLocalTransaction(Message msg,
Object arg) {
String orderId = msg.getKeys();
try {
// 1. 执行本地事务(如插入订单)
boolean success = createOrder(orderId);
if (success) {
// 事务成功,提交消息
return LocalTransactionState.COMMIT_MESSAGE;
} else {
// 事务失败,回滚消息
return LocalTransactionState.ROLLBACK_MESSAGE;
}
} catch (Exception e) {
// 异常,回滚消息
return LocalTransactionState.ROLLBACK_MESSAGE;
}
}
// 事务状态回查
@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
String orderId = msg.getKeys();
// 查询订单是否创建成功
boolean orderExists = checkOrderExists(orderId);
if (orderExists) {
return LocalTransactionState.COMMIT_MESSAGE;
} else {
return LocalTransactionState.ROLLBACK_MESSAGE;
}
}
};
// 创建事务生产者
TransactionMQProducer producer = new TransactionMQProducer(
"ORDER_TX_PRODUCER_GROUP");
producer.setNamesrvAddr("localhost:9876");
producer.setTransactionListener(transactionListener);
// 配置事务回查线程池
ExecutorService executorService = new ThreadPoolExecutor(
2, 5, 100, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2000),
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("tx-check-thread");
return thread;
}
});
producer.setExecutorService(executorService);
producer.start();
// 发送事务消息
String orderId = "ORDER_" + System.currentTimeMillis();
Message msg = new Message("ORDER_TOPIC", "TagA", orderId,
"Order content".getBytes());
TransactionSendResult result = producer.sendMessageInTransaction(
msg, orderId);
System.out.println("事务消息发送结果: " + result.getSendStatus());
System.out.println("本地事务状态: " + result.getLocalTransactionState());
// 等待回查完成
Thread.sleep(10000);
producer.shutdown();
}
private static boolean createOrder(String orderId) {
// 模拟创建订单
System.out.println("创建订单: " + orderId);
return true;
}
private static boolean checkOrderExists(String orderId) {
// 模拟查询订单
System.out.println("回查订单状态: " + orderId);
return true;
}
}
6.2 延迟消息
/**
* 延迟消息示例
*
* 应用场景:订单超时取消、定时任务
*/
public class DelayMessageDemo {
public static void main(String[] args) throws Exception {
DefaultMQProducer producer = new DefaultMQProducer("DELAY_PRODUCER");
producer.setNamesrvAddr("localhost:9876");
producer.start();
// 发送延迟消息
Message msg = new Message("ORDER_TOPIC", "Order timeout check".getBytes());
msg.setKeys("ORDER_123");
// 设置延迟级别
// 1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
// 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
msg.setDelayTimeLevel(16); // 30 分钟后投递
SendResult result = producer.send(msg);
System.out.println("延迟消息发送成功: " + result.getMsgId());
producer.shutdown();
}
/**
* 自定义延迟时间(RocketMQ 5.0+)
*/
public static void sendWithCustomDelay() throws Exception {
DefaultMQProducer producer = new DefaultMQProducer("DELAY_PRODUCER");
producer.setNamesrvAddr("localhost:9876");
producer.start();
Message msg = new Message("ORDER_TOPIC", "Custom delay".getBytes());
// 设置任意延迟时间(秒)
msg.setDelayTimeSec(600); // 10 分钟
SendResult result = producer.send(msg);
System.out.println("自定义延迟消息发送成功: " + result.getMsgId());
producer.shutdown();
}
}
/**
* 延迟消息消费者
*/
class DelayMessageConsumer {
public static void main(String[] args) throws Exception {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(
"DELAY_CONSUMER_GROUP");
consumer.setNamesrvAddr("localhost:9876");
consumer.subscribe("ORDER_TOPIC", "*");
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(
List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
for (MessageExt msg : msgs) {
// 检查是否超时
long storeTime = msg.getStoreTimestamp();
long currentTime = System.currentTimeMillis();
long delayTime = currentTime - storeTime;
System.out.printf("延迟消息: Key=%s, 延迟时间=%dms%n",
msg.getKeys(), delayTime);
// 执行超时逻辑(如取消订单)
cancelOrderIfUnpaid(msg.getKeys());
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();
System.out.println("延迟消息消费者启动成功");
Thread.sleep(Long.MAX_VALUE);
}
private static void cancelOrderIfUnpaid(String orderId) {
System.out.println("检查并取消未支付订单: " + orderId);
}
}
6.3 消息过滤
/**
* 消息过滤示例
*/
public class MessageFilterDemo {
/**
* Tag 过滤
*/
public static void tagFilter() throws Exception {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(
"TAG_FILTER_GROUP");
consumer.setNamesrvAddr("localhost:9876");
// 只订阅 TagA 或 TagB
consumer.subscribe("ORDER_TOPIC", "TagA || TagB");
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(
List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
for (MessageExt msg : msgs) {
System.out.printf("Tag过滤: %s%n", msg.getTags());
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();
}
/**
* SQL92 过滤
*/
public static void sqlFilter() throws Exception {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(
"SQL_FILTER_GROUP");
consumer.setNamesrvAddr("localhost:9876");
// SQL92 表达式过滤
// 需要 Broker 开启 propertyFilter 属性
consumer.subscribe("ORDER_TOPIC",
MessageSelector.bySql("userId = '12345' AND amount > 100"));
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(
List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
for (MessageExt msg : msgs) {
System.out.printf("SQL过滤: userId=%s, amount=%s%n",
msg.getUserProperty("userId"),
msg.getUserProperty("amount"));
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();
}
/**
* 发送带属性的消息
*/
public static void sendMessageWithProperty() throws Exception {
DefaultMQProducer producer = new DefaultMQProducer("PROPERTY_PRODUCER");
producer.setNamesrvAddr("localhost:9876");
producer.start();
Message msg = new Message("ORDER_TOPIC", "Order content".getBytes());
// 设置用户属性
msg.putUserProperty("userId", "12345");
msg.putUserProperty("amount", "200");
msg.putUserProperty("region", "CN");
SendResult result = producer.send(msg);
System.out.println("发送成功: " + result.getMsgId());
producer.shutdown();
}
}
6.4 消息轨迹与监控
/**
* 消息轨迹
*/
public class MessageTraceDemo {
public static void main(String[] args) throws Exception {
// 生产者开启轨迹
DefaultMQProducer producer = new DefaultMQProducer(
"TRACE_PRODUCER", true); // true = 开启轨迹
producer.setNamesrvAddr("localhost:9876");
producer.start();
Message msg = new Message("ORDER_TOPIC", "Trace message".getBytes());
SendResult result = producer.send(msg);
System.out.println("发送成功: " + result.getMsgId());
// 消费者开启轨迹
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(
"TRACE_CONSUMER", true); // true = 开启轨迹
consumer.setNamesrvAddr("localhost:9876");
consumer.subscribe("ORDER_TOPIC", "*");
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(
List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
for (MessageExt msg : msgs) {
System.out.println("消费消息: " + msg.getKeys());
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();
// 轨迹数据会发送到 RMQ_SYS_TRACE_TOPIC
// 可以通过 RocketMQ Console 查看
}
}
七、Spring Boot 集成
7.1 依赖配置
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.2.3</version>
</dependency>
7.2 配置文件
# application.yml
rocketmq:
name-server: localhost:9876
producer:
group: order-producer-group
send-message-timeout: 3000
retry-times-when-send-failed: 3
compress-message-body-threshold: 4096
max-message-size: 4194304
7.3 生产者服务
@Service
@Slf4j
public class RocketMQProducerService {
@Autowired
private RocketMQTemplate rocketMQTemplate;
private static final String ORDER_TOPIC = "ORDER_TOPIC";
/**
* 同步发送
*/
public SendResult sendSync(String topic, String message) {
SendResult result = rocketMQTemplate.syncSend(topic, message);
log.info("同步发送结果: {}", result.getMsgId());
return result;
}
/**
* 异步发送
*/
public void sendAsync(String topic, String message) {
rocketMQTemplate.asyncSend(topic, message, new SendCallback() {
@Override
public void onSuccess(SendResult result) {
log.info("异步发送成功: {}", result.getMsgId());
}
@Override
public void onException(Throwable e) {
log.error("异步发送失败", e);
}
});
}
/**
* 单向发送
*/
public void sendOneWay(String topic, String message) {
rocketMQTemplate.sendOneWay(topic, message);
}
/**
* 发送延迟消息
*/
public SendResult sendDelay(String topic, String message, int delayLevel) {
Message<String> msg = MessageBuilder.withPayload(message).build();
SendResult result = rocketMQTemplate.syncSend(
topic, msg, 3000, delayLevel);
log.info("延迟消息发送结果: {}", result.getMsgId());
return result;
}
/**
* 发送顺序消息
*/
public SendResult sendOrderly(String topic, String message, String key) {
Message<String> msg = MessageBuilder.withPayload(message)
.setHeader(RocketMQHeaders.KEYS, key)
.build();
SendResult result = rocketMQTemplate.syncSendOrderly(
topic, msg, key);
log.info("顺序消息发送结果: {}", result.getMsgId());
return result;
}
/**
* 发送事务消息
*/
public SendResult sendTransaction(String topic, String message, Object arg) {
Message<String> msg = MessageBuilder.withPayload(message).build();
TransactionSendResult result = rocketMQTemplate.sendMessageInTransaction(
topic, msg, arg);
log.info("事务消息发送结果: {}", result.getSendStatus());
return result;
}
}
7.4 消费者服务
@Service
@Slf4j
@RocketMQMessageListener(
topic = "ORDER_TOPIC",
consumerGroup = "ORDER_CONSUMER_GROUP",
messageModel = MessageModel.CLUSTERING
)
public class RocketMQConsumerService implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
log.info("收到消息: {}", message);
try {
// 处理消息
processOrder(message);
} catch (Exception e) {
log.error("处理消息失败", e);
throw e; // 抛出异常,触发重试
}
}
private void processOrder(String message) {
// 处理订单逻辑
log.info("处理订单: {}", message);
}
}
/**
* 带参数的消费者
*/
@RocketMQMessageListener(
topic = "ORDER_TOPIC",
consumerGroup = "ORDER_TAG_CONSUMER_GROUP",
selectorExpression = "TagA || TagB" // Tag 过滤
)
class TagMessageConsumer implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
System.out.println("收到 Tag 消息: " + message);
}
}
/**
* 顺序消息消费者
*/
@RocketMQMessageListener(
topic = "ORDER_TOPIC",
consumerGroup = "ORDER_ORDERLY_CONSUMER_GROUP",
consumeMode = ConsumeMode.ORDERLY // 顺序消费
)
class OrderlyMessageConsumer implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
System.out.println("顺序消费消息: " + message);
}
}
/**
* 广播消费
*/
@RocketMQMessageListener(
topic = "ORDER_TOPIC",
consumerGroup = "ORDER_BROADCAST_CONSUMER_GROUP",
messageModel = MessageModel.BROADCASTING // 广播模式
)
class BroadcastMessageConsumer implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
System.out.println("广播消费消息: " + message);
}
}
/**
* 获取消息详细信息
*/
@RocketMQMessageListener(
topic = "ORDER_TOPIC",
consumerGroup = "ORDER_DETAIL_CONSUMER_GROUP"
)
class DetailMessageConsumer implements RocketMQListener<MessageExt> {
@Override
public void onMessage(MessageExt message) {
System.out.printf("Topic: %s, Tag: %s, Key: %s, Body: %s%n",
message.getTopic(),
message.getTags(),
message.getKeys(),
new String(message.getBody()));
}
}
八、最佳实践
8.1 生产者最佳实践
/**
* 生产者最佳实践
*/
public class ProducerBestPractices {
// 1. 合理设置 GroupName
// - 一个应用使用一个 GroupName
// - GroupName 唯一标识一类生产者
// 2. 消息 Key 设置
// - 业务唯一标识
// - 便于消息查询和排查
Message msg = new Message("Topic", "Key", "Body".getBytes());
// 3. 发送失败重试
// - 同步发送:设置 retryTimesWhenSendFailed
// - 异步发送:设置 retryTimesWhenSendAsyncFailed
// 4. 批量发送
// - 相同 Topic 的消息批量发送
// - 单批次大小不超过 4MB
// - 单批次消息不超过 4KB 可压缩
// 5. 选择合适的发送方式
// - 同步:可靠性要求高
// - 异步:吞吐量要求高
// - 单向:日志等可丢失场景
// 6. 合理设置超时时间
// - 默认 3000ms,根据网络环境调整
}
8.2 消费者最佳实践
/**
* 消费者最佳实践
*/
public class ConsumerBestPractices {
// 1. 消费幂等
// - 消息可能重复投递
// - 业务层实现幂等处理
public ConsumeConcurrentlyStatus consumeMessage(
List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
for (MessageExt msg : msgs) {
String key = msg.getKeys();
// 幂等检查
if (isProcessed(key)) {
continue; // 跳过已处理的消息
}
// 处理消息
process(msg);
// 记录处理状态
markProcessed(key);
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
// 2. 消费超时处理
// - 单条消息处理时间不宜过长
// - 超时会导致重复消费
// - 耗时操作异步处理
// 3. 异常处理
// - 返回 RECONSUME_LATER 触发重试
// - 记录异常日志
// - 死信队列处理
// 4. 批量消费
// - 合理设置 consumeMessageBatchMaxSize
// - 批量处理提高效率
// 5. 消费进度保存
// - 集群模式:Broker 保存
// - 广播模式:本地保存
// 6. 消费线程数
// - 根据 IO/CPU 密集程度调整
// - 默认 20-64 线程
}
8.3 Topic 设计
Topic 设计建议:
1. 命名规范
- 格式:<业务域>_<消息类型>_<版本>
- 例如:ORDER_EVENT_V1, USER_CHANGE_V1
2. 分区数量
- Queue 数量 >= 消费者数量
- 考虑吞吐量需求
- 不宜过多(影响性能)
3. Tag 使用
- 同一业务类型使用 Tag 区分
- 减少 Topic 数量
- 例:ORDER_TOPIC 下 TagA=创建, TagB=取消
4. 分区策略
- 顺序消息:使用相同 Key
- 普通消息:均匀分布
5. 副本配置
- 生产环境至少 2 副本
- Master-Slave 模式
- 异步复制 vs 同步双写
九、总结
RocketMQ 核心优势
- 高可靠:事务消息、消息轨迹、死信队列
- 高可用:主从复制、故障转移
- 丰富特性:延迟消息、顺序消息、消息过滤
- 金融级别:经历过双十一考验
使用场景
| 场景 | 推荐消息类型 |
|---|---|
| 订单支付 | 事务消息 |
| 订单超时取消 | 延迟消息 |
| 订单状态流 | 顺序消息 |
| 日志收集 | 普通消息 |
| 配置推送 | 广播消息 |
常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 消息丢失 | Broker 故障 | 多副本、同步刷盘 |
| 消息重复 | 网络抖动重试 | 消费者幂等 |
| 消息积压 | 消费慢 | 增加消费者、异步处理 |
| 顺序错乱 | 多队列并发 | 使用顺序消息 |
六、思考与练习
思考题
-
基础题:RocketMQ的NameServer与Kafka的Zookeeper在功能上有什么区别?为什么RocketMQ选择不使用Zookeeper?
-
进阶题:RocketMQ的事务消息如何保证本地事务与消息发送的原子性?事务回查机制的工作流程是什么?
-
实战题:对比RocketMQ、Kafka、RabbitMQ的事务消息支持能力,在分布式事务场景下应该如何选择?
编程练习
练习:使用Spring Boot集成RocketMQ实现一个完整的订单系统,包含:(1) 事务消息保证下单与库存扣减的一致性;(2) 延迟消息实现订单30分钟超时取消;(3) 顺序消息保证同一订单的状态变更有序;(4) SQL过滤实现不同消费者的消息分发。
章节关联
- 前置章节:Kafka核心原理与实战、分布式事务详解
- 后续章节:消息可靠性保证详解
- 扩展阅读:RocketMQ官方文档、《RocketMQ技术内幕》
📝 下一章预告
无论使用哪种消息队列,消息的可靠性保障都是核心问题。下一章将深入分析消息丢失、重复、乱序的根本原因,并提供幂等设计、消息去重、补偿机制等完整的解决方案。
本章完
参考资料:
- Apache RocketMQ 官方文档:rocketmq.apache.org/docs/
- RocketMQ Spring Boot 文档:github.com/apache/rock…
- RocketMQ 原理简介:rocketmq.apache.org/docs/rmq-ar…