概述
公司项目完成 RocketMQ 5.x 的集成和封装,提供了类型安全、易于使用的消息队列组件。
核心组件
bash
order-justgotrip-business/
└── com.justgotrip.hotel.booking.mq/
├── message/
│ ├── RocketMQMessage.java # 通用消息实体(泛型)
│ └── DelayLevel.java # 延迟级别枚举(18级)
├── sender/
│ ├── RocketMQSender.java # 消息发送器封装
│ ├── RocketMQDelaySender.java # 延迟消息发送器
│ └── RocketMQTransactionSender.java # 事务消息发送器
├── consumer/
│ └── RocketMQConsumer.java # 消费者工具类
├── OrderMessageProducer.java # 订单消息生产者示例
└── OrderMessageListener.java # 订单消息消费者示例
特性
- ✅ 泛型支持: 类型安全的消息体
- ✅ 批量消息: 支持批量发送提升吞吐量
- ✅ 延迟消息: 18个延迟级别(1秒~2小时),支持自动计算
- ✅ 事务消息: 分布式事务一致性保证
- ✅ 顺序消息: 保证消息顺序消费
- ✅ 异步发送: 提升发送性能
- ✅ 消息追踪: 自动生成任务ID
- ✅ 扩展信息: 支持自定义元数据
快速开始
1. 配置文件
application.yml
yaml
rocketmq:
# NameServer 地址
name-server: 127.0.0.1:9876
# 生产者配置
producer:
group: hotel-booking-producer
send-message-timeout: 3000
max-message-size: 4194304
compress-message-body-threshold: 4096
# 消费者配置
consumer:
# Job模块消费者组
group: hotel-booking-job-consumer
# Business模块消费者组
business-group: hotel-booking-business-consumer
message-model: CLUSTERING
consume-thread-min: 20
consume-thread-max: 64
consume-message-batch-max-size: 1
2. 添加依赖(已完成)
order-justgotrip-business/pom.xml
xml
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client-java</artifactId>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
</dependency>
3. Hello World
发送消息
java
@Service
public class OrderService {
private final RocketMQSender rocketMQSender;
public void sendOrderMessage() {
// 构建消息
RocketMQMessage<OrderDTO> message = RocketMQSender.buildMessage(
"hotel-order-topic", // topic
"order_create", // tag
orderDTO // body
);
// 同步发送
SendResult result = rocketMQSender.send(message);
System.out.println("消息发送成功: " + result.getMsgId());
}
}
消费消息
java
@Component
@RocketMQMessageListener(
topic = "hotel-order-topic",
consumerGroup = "hotel-booking-business-consumer"
)
public class OrderMessageListener implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
log.info("收到消息: {}", message);
// 处理业务逻辑
}
}
消息实体详解
RocketMQMessage 核心属性
java
public class RocketMQMessage<T> implements Serializable {
private String topic; // 主题(必填)
private String tag; // 标签(可选,用于过滤)
private String key; // 消息Key(可选,用于幂等和查询)
private DelayLevel delayLevel; // 延迟级别
private T message; // 单条消息体
private List<T> messages; // 批量消息体
private String hashKey; // 有序消息的HashKey
private String taskId; // 任务ID(自动生成)
private Map<String, Object> ext; // 扩展信息
}
延迟级别枚举
| 级别 | 延迟时间 | 使用场景 |
|---|---|---|
| OFF | 不延迟 | 默认 |
| LEVEL_1 | 1秒 | 短暂延迟 |
| LEVEL_2 | 5秒 | 重试延迟 |
| LEVEL_3 | 10秒 | 补偿延迟 |
| LEVEL_4 | 30秒 | 订单超时检查 |
| LEVEL_5 | 1分钟 | 支付超时 |
| LEVEL_6 | 2分钟 | 状态同步延迟 |
| LEVEL_9 | 5分钟 | 定时任务 |
| LEVEL_14 | 10分钟 | 长延迟任务 |
| LEVEL_16 | 30分钟 | 半小时延迟 |
| LEVEL_17 | 1小时 | 一小时延迟 |
| LEVEL_18 | 2小时 | 两小时延迟 |
Builder 模式构建消息
java
RocketMQMessage<OrderDTO> message = RocketMQMessage.<OrderDTO>builder()
.topic("hotel-order-topic") // 必填
.tag("order_status") // 可选
.key(orderId.toString()) // 可选,用于幂等
.message(orderDTO) // 单条消息
.delayLevel(DelayLevel.LEVEL_5) // 延迟1分钟
.taskId("custom-task-id") // 可选,不填则自动生成
.putExt("traceId", traceId) // 扩展信息
.putExt("source", "API") // 扩展信息
.build();
消息发送
1. 同步发送
适用场景: 重要消息,需要确认发送成功
java
@Service
public class OrderService {
private final RocketMQSender rocketMQSender;
public void createOrder(OrderDTO orderDTO) {
// 构建消息
RocketMQMessage<OrderDTO> message = RocketMQSender.buildMessage(
"hotel-order-topic",
"order_create",
orderDTO
);
// 同步发送
SendResult result = rocketMQSender.send(message);
log.info("订单创建消息发送成功, msgId: {}, orderId: {}",
result.getMsgId(), orderDTO.getOrderId());
}
}
2. 异步发送
适用场景: 高并发场景,不阻塞主线程
java
@Service
public class OrderService {
private final RocketMQSender rocketMQSender;
public void updateOrderStatusAsync(Long orderId, String status) {
Map<String, Object> body = new HashMap<>();
body.put("orderId", orderId);
body.put("status", status);
body.put("timestamp", System.currentTimeMillis());
RocketMQMessage<Map<String, Object>> message =
RocketMQSender.buildMessage("hotel-order-topic", "order_status", body);
// 异步发送
rocketMQSender.sendAsync(message, new SendCallback() {
@Override
public void onSuccess(SendResult result) {
log.info("订单状态消息发送成功 - orderId:{}, msgId:{}",
orderId, result.getMsgId());
}
@Override
public void onException(Throwable e) {
log.error("订单状态消息发送失败 - orderId:{}", orderId, e);
// 可以在这里加入重试逻辑
}
});
}
}
3. 批量发送
适用场景: 需要发送大量消息,提升吞吐量
java
@Service
public class BatchOrderService {
private final RocketMQSender rocketMQSender;
public void batchCreateOrders(List<OrderDTO> orders) {
// 构建批量消息
RocketMQMessage<OrderDTO> message = RocketMQSender.buildBatchMessage(
"hotel-order-topic",
"order_batch",
orders
);
// 批量发送
SendResult result = rocketMQSender.sendBatch(message);
log.info("批量订单消息发送成功, count:{}, msgId:{}",
orders.size(), result.getMsgId());
}
}
4. 延迟消息
适用场景: 定时任务、超时检查、延迟处理
java
@Service
public class PaymentTimeoutService {
private final RocketMQSender rocketMQSender;
public void createOrderWithPaymentCheck(OrderDTO orderDTO) {
// 1. 发送订单创建消息(立即)
RocketMQMessage<OrderDTO> createMsg = RocketMQSender.buildMessage(
"hotel-order-topic",
"order_create",
orderDTO
);
rocketMQSender.send(createMsg);
// 2. 发送支付超时检查消息(延迟30分钟)
Map<String, Object> timeoutBody = new HashMap<>();
timeoutBody.put("orderId", orderDTO.getOrderId());
timeoutBody.put("checkType", "PAYMENT_TIMEOUT");
RocketMQMessage<Map<String, Object>> timeoutMsg =
RocketMQSender.buildDelayMessage(
"hotel-order-topic",
"order_timeout",
timeoutBody,
DelayLevel.LEVEL_16 // 30分钟
);
rocketMQSender.sendDelay(timeoutMsg, DelayLevel.LEVEL_16);
}
}
5. 顺序消息
适用场景: 需要保证消息顺序(同一订单的消息按顺序消费)
java
@Service
public class OrderStatusService {
private final RocketMQSender rocketMQSender;
public void updateOrderStatus(Long orderId, String status) {
Map<String, Object> body = new HashMap<>();
body.put("orderId", orderId);
body.put("status", status);
body.put("timestamp", System.currentTimeMillis());
// 使用 orderId 作为 hashKey,保证同一订单的消息进入同一队列
RocketMQMessage<Map<String, Object>> message =
RocketMQSender.buildOrderlyMessage(
"hotel-order-topic",
"order_status",
body,
orderId.toString() // hashKey
);
rocketMQSender.sendOrderly(message);
}
}
6. 单向发送
适用场景: 不关心发送结果,追求最高性能
java
@Service
public class LogService {
private final RocketMQSender rocketMQSender;
public void sendLog(LogEntry log) {
RocketMQMessage<LogEntry> message = RocketMQSender.buildMessage(
"log-topic",
"operation_log",
log
);
// 单向发送,不等待响应
rocketMQSender.sendOneWay(message);
}
}
消息消费
1. 基础消费者
java
@Component
@RocketMQMessageListener(
topic = "hotel-order-topic",
consumerGroup = "hotel-booking-business-consumer"
)
public class OrderMessageListener implements RocketMQListener<String> {
private static final Logger log = LoggerFactory.getLogger(OrderMessageListener.class);
@Override
public void onMessage(String message) {
log.info("收到订单消息: {}", message);
// 解析消息
Map<String, Object> msgMap = JSON.parseObject(message, Map.class);
Long orderId = (Long) msgMap.get("orderId");
String status = (String) msgMap.get("status");
// 处理业务逻辑
handleOrderStatusChange(orderId, status);
}
private void handleOrderStatusChange(Long orderId, String status) {
// 业务处理逻辑
}
}
2. 使用消费者工具类
java
@Component
@RocketMQMessageListener(
topic = "hotel-order-topic",
consumerGroup = "hotel-booking-business-consumer"
)
public class EnhancedOrderListener implements RocketMQListener<String> {
private static final Logger log = LoggerFactory.getLogger(EnhancedOrderListener.class);
@Override
public void onMessage(String message) {
// 创建消费者上下文
RocketMQConsumer.ConsumerContext context = RocketMQConsumer.createContext(
"hotel-order-topic",
null, // tag
"hotel-booking-business-consumer"
);
// 记录接收日志
RocketMQConsumer.logReceived(message, context);
long startTime = System.currentTimeMillis();
try {
// 解析并处理消息
processMessage(message);
// 记录成功日志
RocketMQConsumer.logSuccess(message, System.currentTimeMillis() - startTime);
} catch (Exception e) {
// 记录错误日志
RocketMQConsumer.logError(message, e);
throw e;
}
}
private void processMessage(String message) {
// 业务处理逻辑
}
}
3. 按Tag过滤消费
java
// 消费者1:只处理订单创建消息
@Component
@RocketMQMessageListener(
topic = "hotel-order-topic",
selectorExpression = "order_create",
consumerGroup = "order-create-consumer"
)
public class OrderCreateListener implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
log.info("处理订单创建: {}", message);
// 只处理 order_create tag 的消息
}
}
// 消费者2:只处理订单状态变更消息
@Component
@RocketMQMessageListener(
topic = "hotel-order-topic",
selectorExpression = "order_status",
consumerGroup = "order-status-consumer"
)
public class OrderStatusListener implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
log.info("处理订单状态变更: {}", message);
// 只处理 order_status tag 的消息
}
}
4. 批量消费
java
@Component
@RocketMQMessageListener(
topic = "hotel-order-topic",
consumerGroup = "batch-consumer",
consumeThreadNumber = 10, // 消费线程数
consumeThreadMax = 20
)
public class BatchOrderListener implements RocketMQListener<List<MessageExt>> {
@Override
public void onMessage(List<MessageExt> messages) {
log.info("批量收到消息, count: {}", messages.size());
List<OrderDTO> orders = new ArrayList<>();
for (MessageExt msg : messages) {
String body = new String(msg.getBody(), StandardCharsets.UTF_8);
OrderDTO order = JSON.parseObject(body, OrderDTO.class);
orders.add(order);
}
// 批量处理
batchProcessOrders(orders);
}
}
高级特性
1. 消息幂等性
使用 key 实现幂等性:
java
@Service
public class OrderService {
private final RocketMQSender rocketMQSender;
private final RedisTemplate<String, String> redisTemplate;
public void sendOrderMessageWithIdempotent(OrderDTO orderDTO) {
String messageId = "ORDER_" + orderDTO.getOrderId();
// 检查是否已处理
Boolean processed = redisTemplate.hasKey("mq:processed:" + messageId);
if (Boolean.TRUE.equals(processed)) {
log.warn("消息已处理,跳过 - messageId: {}", messageId);
return;
}
// 发送消息时设置key
RocketMQMessage<OrderDTO> message = RocketMQMessage.<OrderDTO>builder()
.topic("hotel-order-topic")
.tag("order_create")
.key(messageId) // 用于幂等
.message(orderDTO)
.build();
rocketMQSender.send(message);
// 标记已处理
redisTemplate.opsForValue().set("mq:processed:" + messageId, "1", 24, TimeUnit.HOURS);
}
}
2. 消息追踪
使用 taskId 和 ext 追踪消息链路:
java
@Service
public class TraceableOrderService {
private final RocketMQSender rocketMQSender;
public void sendTraceableOrder(OrderDTO orderDTO, String traceId) {
RocketMQMessage<OrderDTO> message = RocketMQMessage.<OrderDTO>builder()
.topic("hotel-order-topic")
.tag("order_create")
.message(orderDTO)
.taskId(traceId) // 设置追踪ID
.putExt("traceId", traceId)
.putExt("userId", orderDTO.getUserId())
.putExt("clientIp", getClientIp())
.putExt("timestamp", System.currentTimeMillis())
.build();
rocketMQSender.send(message);
}
}
3. 消息重试
java
@Service
public class RetryableOrderService {
private final RocketMQSender rocketMQSender;
private static final int MAX_RETRY = 3;
public void sendWithRetry(OrderDTO orderDTO) {
sendWithRetry(orderDTO, 0);
}
private void sendWithRetry(OrderDTO orderDTO, int retryCount) {
try {
RocketMQMessage<OrderDTO> message = RocketMQSender.buildMessage(
"hotel-order-topic",
"order_create",
orderDTO
);
rocketMQSender.send(message);
} catch (Exception e) {
if (retryCount < MAX_RETRY) {
log.warn("消息发送失败,重试 {}/{} - orderId:{}",
retryCount + 1, MAX_RETRY, orderDTO.getOrderId());
// 延迟重试
try {
Thread.sleep(1000 * (retryCount + 1));
} catch (InterruptedException ignored) {}
sendWithRetry(orderDTO, retryCount + 1);
} else {
log.error("消息发送失败,已达最大重试次数 - orderId:{}",
orderDTO.getOrderId(), e);
// 可以写入死信队列或数据库
saveToDeadQueue(orderDTO);
}
}
}
}
4. 事务消息(半消息)
java
@Service
public class TransactionalOrderService {
private final RocketMQTemplate rocketMQTemplate;
public void sendTransactionalMessage(OrderDTO orderDTO) {
// 发送半消息
rocketMQTemplate.sendMessageInTransaction(
new TransactionListener() {
@Override
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
try {
// 执行本地事务(创建订单)
createOrderInDB(orderDTO);
return LocalTransactionState.COMMIT_MESSAGE;
} catch (Exception e) {
log.error("本地事务执行失败", e);
return LocalTransactionState.ROLLBACK_MESSAGE;
}
}
@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
// 回查本地事务状态
String orderId = msg.getKeys();
if (orderExistsInDB(orderId)) {
return LocalTransactionState.COMMIT_MESSAGE;
} else {
return LocalTransactionState.ROLLBACK_MESSAGE;
}
}
},
MessageBuilder.withPayload(orderDTO).build(),
null
);
}
}
延迟消息详解
RocketMQ 提供了 18 个延迟级别,可以满足大部分延迟场景。本项目提供了 RocketMQDelaySender 专门处理延迟消息。
1. 延迟级别枚举
java
public enum DelayLevel {
OFF(0, "不延迟"),
LEVEL_1(1, "1秒"),
LEVEL_2(2, "5秒"),
LEVEL_3(3, "10秒"),
LEVEL_4(4, "30秒"),
LEVEL_5(5, "1分钟"),
LEVEL_6(6, "2分钟"),
LEVEL_7(7, "3分钟"),
LEVEL_8(8, "4分钟"),
LEVEL_9(9, "5分钟"),
LEVEL_10(10, "6分钟"),
LEVEL_11(11, "7分钟"),
LEVEL_12(12, "8分钟"),
LEVEL_13(13, "9分钟"),
LEVEL_14(14, "10分钟"),
LEVEL_15(15, "20分钟"),
LEVEL_16(16, "30分钟"),
LEVEL_17(17, "1小时"),
LEVEL_18(18, "2小时");
}
2. 使用延迟发送器
注入 RocketMQDelaySender
java
@Service
public class OrderService {
private final RocketMQDelaySender delaySender;
public OrderService(RocketMQDelaySender delaySender) {
this.delaySender = delaySender;
}
}
3. 发送延迟消息(指定级别)
java
@Service
public class PaymentTimeoutService {
private final RocketMQDelaySender delaySender;
// 发送30分钟后执行的支付超时检查
public void sendPaymentTimeoutCheck(Long orderId) {
Map<String, Object> body = new HashMap<>();
body.put("orderId", orderId);
body.put("checkType", "PAYMENT_TIMEOUT");
RocketMQMessage<Map<String, Object>> message =
RocketMQMessage.<Map<String, Object>>builder()
.topic("hotel-order-topic")
.tag("payment_timeout")
.key("PAYMENT_TIMEOUT_" + orderId)
.message(body)
.build();
// 延迟30分钟
delaySender.sendDelay(message, DelayLevel.LEVEL_16);
}
}
4. 自动计算延迟级别
按时间单位发送
java
@Service
public class FlexibleDelayService {
private final RocketMQDelaySender delaySender;
// 延迟45秒执行(自动选择 LEVEL_4: 30秒)
public void sendDelayWithSeconds(RocketMQMessage<?> message) {
delaySender.sendDelaySeconds(message, 45);
}
// 延迟15分钟执行(自动选择 LEVEL_15: 20分钟)
public void sendDelayWithMinutes(RocketMQMessage<?> message) {
delaySender.sendDelayMinutes(message, 15);
}
// 延迟3小时执行(自动选择 LEVEL_18: 2小时)
public void sendDelayWithHours(RocketMQMessage<?> message) {
delaySender.sendDelayHours(message, 3);
}
}
5. 定时延迟消息(指定时间点执行)
java
@Service
public class ScheduledTaskService {
private final RocketMQDelaySender delaySender;
// 在指定时间点执行任务
public void scheduleTaskAtTime(Long orderId, Date executeTime) {
Map<String, Object> body = new HashMap<>();
body.put("orderId", orderId);
body.put("taskType", "SCHEDULED_TASK");
RocketMQMessage<Map<String, Object>> message =
RocketMQMessage.<Map<String, Object>>builder()
.topic("hotel-order-topic")
.tag("scheduled_task")
.key("TASK_" + orderId)
.message(body)
.build();
delaySender.sendDelayAt(message, executeTime);
}
}
6. 业务场景封装
订单超时检查
java
@Service
public class OrderTimeoutService {
private final RocketMQDelaySender delaySender;
// 创建订单时发送30分钟后的超时检查
public void createOrderWithTimeout(OrderDTO orderDTO) {
// 1. 发送订单创建消息
// ...
// 2. 发送30分钟后的超时检查
delaySender.sendOrderTimeoutCheck(orderDTO.getOrderId(), 30);
}
}
支付超时检查
java
@Service
public class PaymentService {
private final RocketMQDelaySender delaySender;
// 用户下单时,发送支付超时检查消息
public void waitForPayment(Long orderId, int timeoutMinutes) {
delaySender.sendPaymentTimeoutCheck(orderId, timeoutMinutes);
}
}
订单自动确认
java
@Service
public class OrderConfirmService {
private final RocketMQDelaySender delaySender;
// 订单完成后,24小时自动确认
public void scheduleAutoConfirm(Long orderId) {
delaySender.sendOrderAutoConfirm(orderId, 24);
}
}
7. 延迟消息消费
java
@Component
@RocketMQMessageListener(
topic = "hotel-order-topic",
selectorExpression = "payment_timeout || order_timeout",
consumerGroup = "timeout-check-consumer"
)
public class TimeoutCheckListener implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
log.info("收到超时检查消息: {}", message);
Map<String, Object> msgMap = JSON.parseObject(message, Map.class);
String checkType = (String) msgMap.get("checkType");
Long orderId = (Long) msgMap.get("orderId");
switch (checkType) {
case "PAYMENT_TIMEOUT":
handlePaymentTimeout(orderId);
break;
case "ORDER_TIMEOUT":
handleOrderTimeout(orderId);
break;
case "AUTO_CONFIRM":
handleAutoConfirm(orderId);
break;
}
}
private void handlePaymentTimeout(Long orderId) {
// 检查订单支付状态
// 如果未支付,自动取消订单
}
private void handleOrderTimeout(Long orderId) {
// 处理订单超时逻辑
}
private void handleAutoConfirm(Long orderId) {
// 自动确认订单
}
}
事务消息详解
RocketMQ 事务消息用于实现分布式事务的一致性,保证本地事务和消息发送的原子性。
1. 事务消息原理
markdown
1. 发送半消息(Half Message)- 消息对消费者不可见
2. 执行本地事务
3. 提交事务状态
- 成功:提交消息(Commit)- 消息对消费者可见
- 失败:回滚消息(Rollback)- 删除消息
- 未知:回查状态(Check)- MQ主动查询本地事务状态
2. 使用事务发送器
注入 RocketMQTransactionSender
java
@Service
public class TransactionalOrderService {
private final RocketMQTransactionSender txSender;
public TransactionalOrderService(RocketMQTransactionSender txSender) {
this.txSender = txSender;
}
}
3. 发送事务消息(使用执行器)
java
@Service
public class OrderService {
private final RocketMQTransactionSender txSender;
private final OrderRepository orderRepository;
@Transactional
public void createOrderTransactional(final OrderDTO orderDTO) {
// 构建事务消息
RocketMQMessage<OrderDTO> message =
RocketMQTransactionSender.buildTransactionMessage(
"hotel-order-topic",
"order_create",
"ORDER_" + orderDTO.getOrderId(),
orderDTO
);
// 发送事务消息
txSender.sendTransactionMessage(message, new RocketMQTransactionSender.LocalTransactionExecutor<OrderDTO>() {
@Override
public void execute(OrderDTO msg) throws Exception {
// 执行本地事务:创建订单
log.info("执行本地事务 - 创建订单: {}", msg.getOrderId());
Order order = new Order();
order.setOrderId(msg.getOrderId());
order.setOrderNo(msg.getOrderNo());
orderRepository.save(order);
}
@Override
public boolean check(OrderDTO msg) throws Exception {
// 回查本地事务状态:检查订单是否存在
log.info("回查本地事务状态 - 检查订单: {}", msg.getOrderId());
return orderRepository.existsById(msg.getOrderId());
}
});
}
}
4. 事务消息消费
java
@Component
@RocketMQMessageListener(
topic = "hotel-order-topic",
selectorExpression = "order_create",
consumerGroup = "order-event-consumer"
)
public class OrderEventListener implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
log.info("收到订单事件消息: {}", message);
OrderDTO orderDTO = JSON.parseObject(message, OrderDTO.class);
// 处理订单后续逻辑
// 例如:通知其他服务、触发工作流等
notifyOtherServices(orderDTO);
}
}
5. 典型应用场景
场景1:订单创建 + 库存扣减
java
@Service
public class OrderInventoryService {
private final RocketMQTransactionSender txSender;
private final OrderRepository orderRepository;
private final InventoryService inventoryService;
/**
* 创建订单并扣减库存(事务消息)
*/
public void createOrderWithInventory(final OrderDTO orderDTO) {
RocketMQMessage<OrderDTO> message =
RocketMQTransactionSender.buildTransactionMessage(
"hotel-order-topic",
"order_create",
"ORDER_" + orderDTO.getOrderId(),
orderDTO
);
txSender.sendTransactionMessage(message, new RocketMQTransactionSender.LocalTransactionExecutor<OrderDTO>() {
@Override
public void execute(OrderDTO msg) throws Exception {
// 本地事务:创建订单 + 扣减库存
// 1. 创建订单
Order order = new Order();
order.setOrderId(msg.getOrderId());
orderRepository.save(order);
// 2. 扣减库存
inventoryService.deduct(msg.getHotelId(), msg.getRoomCount());
// 如果任何一步失败,抛出异常,消息会被回滚
}
@Override
public boolean check(OrderDTO msg) throws Exception {
// 回查:订单和库存状态
return orderRepository.existsById(msg.getOrderId());
}
});
}
}
场景2:支付成功 + 订单状态更新
java
@Service
public class PaymentOrderService {
private final RocketMQTransactionSender txSender;
private final PaymentRepository paymentRepository;
private final OrderRepository orderRepository;
/**
* 支付成功后更新订单状态(事务消息)
*/
public void paymentSuccess(final PaymentDTO paymentDTO) {
RocketMQMessage<PaymentDTO> message =
RocketMQTransactionSender.buildTransactionMessage(
"hotel-order-topic",
"payment_success",
"PAYMENT_" + paymentDTO.getPaymentId(),
paymentDTO
);
txSender.sendTransactionMessage(message, new RocketMQTransactionSender.LocalTransactionExecutor<PaymentDTO>() {
@Override
public void execute(PaymentDTO msg) throws Exception {
// 本地事务:
// 1. 更新支付状态
paymentRepository.updateStatus(msg.getPaymentId(), "SUCCESS");
// 2. 更新订单状态
orderRepository.updateStatus(msg.getOrderId(), "PAID");
}
@Override
public boolean check(PaymentDTO msg) throws Exception {
// 回查:支付和订单状态
return "SUCCESS".equals(paymentRepository.getStatus(msg.getPaymentId()));
}
});
}
}
6. 事务消息注意事项
| 注意事项 | 说明 |
|---|---|
| 幂等性 | 消费者需要实现幂等性,因为事务消息可能重复消费 |
| 回查机制 | 本地事务执行器需要实现 check() 方法,用于 MQ 回查 |
| 超时时间 | 事务消息的超时时间由 transactionTimeout 配置决定 |
| 线程安全 | 本地事务执行器需要保证线程安全 |
| 异常处理 | execute() 抛出异常会导致消息回滚,需谨慎处理 |
7. 配置事务消息
application.yml
yaml
rocketmq:
producer:
# 事务消息超时时间(毫秒)
transaction-timeout: 300000
# 最大重试次数
retry-times-when-send-failed: 2
最佳实践
1. Topic 和 Tag 设计
命名规范
css
Topic: {业务线}-{功能}
Tag: {模块}_{操作}
示例
vbnet
✅ 推荐:
Topic: hotel-order
Tag: order_create
Tag: order_cancel
Tag: order_status_update
❌ 不推荐:
Topic: topic1
Tag: tag
2. 消息体设计
java
// ✅ 推荐:使用专用的DTO
public class OrderCreatedEvent {
private Long orderId;
private String orderNo;
private Long userId;
private BigDecimal amount;
private LocalDateTime createTime;
}
// ❌ 不推荐:使用Map
Map<String, Object> body = new HashMap<>();
body.put("orderId", orderId);
body.put("orderNo", orderNo);
3. 消费者处理
java
@Component
public class BestPracticeListener implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
try {
// 1. 参数校验
if (StringUtils.isEmpty(message)) {
log.warn("空消息,跳过");
return;
}
// 2. 幂等性检查
String msgKey = extractKey(message);
if (isProcessed(msgKey)) {
log.info("消息已处理,跳过 - key:{}", msgKey);
return;
}
// 3. 业务处理
processMessage(message);
// 4. 标记已处理
markAsProcessed(msgKey);
} catch (BusinessException e) {
// 业务异常,记录日志但不抛出异常
log.error("业务处理失败 - message:{}", message, e);
} catch (Exception e) {
// 系统异常,抛出异常触发重试
log.error("系统异常,将重试 - message:{}", message, e);
throw e;
}
}
}
4. 性能优化
yaml
# 生产者配置优化
rocketmq:
producer:
# 压缩阈值,超过4KB自动压缩
compress-message-body-threshold: 4096
# 批量发送大小
max-message-size: 4194304
consumer:
# 消费线程数
consume-thread-min: 20
consume-thread-max: 64
# 批量消费
consume-message-batch-max-size: 1
java
// 批量发送示例
@Service
public class PerformanceOptimizedService {
private final RocketMQSender rocketMQSender;
public void batchSend(List<OrderDTO> orders) {
// 按100条一批发送
Lists.partition(orders, 100).forEach(batch -> {
RocketMQMessage<OrderDTO> message = RocketMQSender.buildBatchMessage(
"hotel-order-topic",
"order_batch",
batch
);
rocketMQSender.sendBatch(message);
});
}
}
常见问题
Q1: 消息发送失败怎么办?
A: 实现重试机制
java
@Service
public class RetryService {
private final RocketMQSender rocketMQSender;
@Retryable(
value = {MQException.class},
maxAttempts = 3,
backoff = @Backoff(delay = 1000, multiplier = 2)
)
public void sendWithRetry(RocketMQMessage<?> message) {
rocketMQSender.send(message);
}
}
Q2: 如何保证消息不丢失?
A: 使用同步发送 + 持久化日志
java
@Service
public class ReliableMessageService {
private final RocketMQSender rocketMQSender;
private final MessageLogRepository logRepository;
@Transactional
public void sendReliableMessage(OrderDTO orderDTO) {
// 1. 先保存消息日志
MessageLog log = new MessageLog();
log.setTopic("hotel-order-topic");
log.setTag("order_create");
log.setBody(JSON.toJSONString(orderDTO));
log.setStatus("SENDING");
logRepository.save(log);
try {
// 2. 发送消息
RocketMQMessage<OrderDTO> message = RocketMQSender.buildMessage(
"hotel-order-topic",
"order_create",
orderDTO
);
SendResult result = rocketMQSender.send(message);
// 3. 更新日志状态
log.setStatus("SENT");
log.setMsgId(result.getMsgId());
logRepository.save(log);
} catch (Exception e) {
// 发送失败,更新状态
log.setStatus("FAILED");
log.setErrorMessage(e.getMessage());
logRepository.save(log);
throw e;
}
}
}
Q3: 消息消费积压怎么处理?
A: 增加消费者实例和线程数
yaml
rocketmq:
consumer:
# 增加消费线程
consume-thread-min: 50
consume-thread-max: 100
bash
# 水平扩展:启动多个消费者实例
# 注意:同组消费者会负载均衡,不同组消费者会广播
java -jar order-justgotrip-job.jar --server.port=9003
java -jar order-justgotrip-job.jar --server.port=9004
java -jar order-justgotrip-job.jar --server.port=9005