通过一个完整的电商场景,从基础到进阶全面掌握在Spring中使用RocketMQ的方法。这是一个循序渐进的教程,旨在帮助你系统性地掌握RocketMQ在微服务架构中的应用。
demo可以通过https://gitee.com/tanxiaomi/interview-demo/blob/master/Interview-demo.rar下载
目录
场景设定:小明的水果店
想象一下,小明开了一家水果店,有收银台和库存管理系统。当顾客在收银台付款后,系统需要做几件事:
- 创建订单记录
- 扣减库存
- 记录操作日志
- 处理高价值订单
- 自动取消超时未支付订单
如果这些操作都串行执行,顾客可能要等很久。这时候,RocketMQ就派上用场了!
基础篇:消息队列入门
什么是消息队列?
消息队列是一种在分布式系统中传递消息的通信方式。它就像一个"邮局",发送方把消息投递到邮局,接收方从邮局取走邮件。
RocketMQ核心概念
- Producer(生产者):发送消息的一方(如收银台服务)
- Consumer(消费者):接收消息的一方(如库存服务)
- Topic(主题):消息的分类,如"订单相关"
- Tag(标签):更细粒度的分类,如"订单创建"、"订单取消"
- Message(消息):具体的数据内容
- Consumer Group(消费组) :消息负载均衡和容错机制,这里有疑惑可以看我另外一篇博客消费组详细介绍
为什么需要消息队列?
- 解耦:服务之间不需要直接调用
- 异步:提升用户体验
- 削峰:应对流量高峰
- 可靠性:确保消息不丢失
项目结构详解
Interview-demo/
├── rocketmq-consumer/ # 订单服务模块
│ ├── src/main/java/
│ │ └── tanxiaomi.rocketmqconsumer/
│ │ ├── OrderService.java # 订单服务
│ │ ├── Order.java # 订单实体
│ │ ├── ConsumeProduct.java # 商品实体
│ │ └── *.java # 各种消费者
│ └── pom.xml
├── rocketmq-producer/ # 收银台服务模块
│ ├── src/main/java/
│ │ └── tanxiaomi.rocketmqproducer/
│ │ ├── OrderService.java # 订单创建服务
│ │ ├── Order.java # 订单实体
│ │ ├── ConsumeProduct.java # 商品实体
│ │ └── *.java # 各种消费者
│ └── pom.xml
└── pom.xml
核心流程实现

1. 收银台下单(消息生产者)
java
// OrderService.java - 收银台服务
@Service
public class OrderService {
@Autowired
private RocketMQTemplate rocketMQTemplate;
public boolean createOrder(String customerId, List<ConsumeProduct> productList) {
// 1. 构造订单信息
StringBuilder productInfo = new StringBuilder();
BigDecimal totalAmount = BigDecimal.ZERO;
for (ConsumeProduct product : productList) {
productInfo.append(product.getName())
.append("(数量:")
.append(product.getNum())
.append(",单价:")
.append(product.getPrice())
.append(");");
BigDecimal price = new BigDecimal(product.getPrice());
BigDecimal num = new BigDecimal(product.getNum());
totalAmount = totalAmount.add(price.multiply(num));
}
// 2. 创建订单对象
String orderId = UUID.randomUUID().toString();
Order order = new Order(orderId, customerId, totalAmount, "CREATED", productInfo.toString());
System.out.println("订单已创建: " + order.toString());
// 3. 发送订单创建消息
try {
// 同步发送订单创建消息
rocketMQTemplate.syncSend("order-topic:ORDER_CREATED",
JSON.toJSONString(order));
System.out.println("订单创建消息已发送: " + order.toString());
// 发送库存扣减消息
sendInventoryDeductionMessage(order, productList);
return true;
} catch (Exception e) {
System.err.println("发送订单创建消息失败: " + e.getMessage());
return false;
}
}
/**
* 发送库存扣减消息
*/
private void sendInventoryDeductionMessage(Order order, List<ConsumeProduct> productList) {
try {
// 构造库存扣减消息内容
String inventoryMessage = JSON.toJSONString(productList);
// 发送库存扣减消息到 inventory-topic 主题
rocketMQTemplate.syncSend("inventory-topic:DEDUCT_INVENTORY", inventoryMessage);
System.out.println("库存扣减消息已发送: " + inventoryMessage);
} catch (Exception e) {
System.err.println("发送库存扣减消息失败: " + e.getMessage());
}
}
}
2. 订单处理服务(消费者)
java
// OrderCreatedConsumer.java
@Component
@RocketMQMessageListener(
topic = "order-topic",
consumerGroup = "order-created-consumer-group",
selectorExpression = "ORDER_CREATED"
)
public class OrderCreatedConsumer implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
System.out.println("[订单创建消费者] 收到订单创建消息: " + message);
// 模拟处理时间
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("[订单创建消费者] 订单创建消息处理完成: " + message);
}
}
3. 库存服务(消费者)
java
// InventoryDeductionConsumer.java
@Component
@RocketMQMessageListener(
topic = "inventory-topic",
consumerGroup = "inventory-deduction-consumer-group",
selectorExpression = "DEDUCT_INVENTORY"
)
public class InventoryDeductionConsumer implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
System.out.println("[库存扣减消费者] 收到库存扣减消息: " + message);
// 模拟库存扣减处理
try {
Thread.sleep(1500);
System.out.println("[库存扣减消费者] 库存扣减处理完成,商品已出库: " + message);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
4. 日志服务(消费者)
java
// OrderLogConsumer.java
@Component
@RocketMQMessageListener(
topic = "order-topic",
consumerGroup = "order-log-consumer-group",
selectorExpression = "ORDER_LOG || ORDER_CREATED"
)
public class OrderLogConsumer implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
System.out.println("[订单日志消费者] 收到订单日志消息: " + message);
// 模拟日志处理时间
try {
Thread.sleep(200);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("[订单日志消费者] 订单日志消息处理完成: " + message);
}
}
5. 高价值订单处理(消费者)
java
// HighValueOrderConsumer.java
@Component
@RocketMQMessageListener(
topic = "order-topic",
consumerGroup = "high-value-order-consumer-group",
selectorExpression = "HIGH_VALUE_ORDER"
)
public class HighValueOrderConsumer implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
System.out.println("[高价值订单消费者] 收到高价值订单消息: " + message);
// 高价值订单需要更仔细的处理
try {
Thread.sleep(2000); // 模拟复杂处理过程
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("[高价值订单消费者] 高价值订单消息处理完成: " + message);
}
}
进阶篇:消息可靠性保障
本地消息表模式
为了解决消息发送的可靠性问题,我们可以使用本地消息表模式:
java
// 本地消息表实体
public class LocalMessage {
private String id;
private String topic;
private String tag;
private String message;
private Integer status; // 0:待发送 1:已发送 2:发送失败
private Integer retryCount;
private Date createTime;
private Date updateTime;
}
// 在业务方法中
@Transactional
public boolean createOrderWithMessageTable(String customerId, List<ConsumeProduct> productList) {
// 1. 执行业务操作(创建订单)
Order order = createOrder(customerId, productList);
// 2. 插入本地消息表
LocalMessage localMessage = new LocalMessage();
localMessage.setId(UUID.randomUUID().toString());
localMessage.setTopic("order-topic");
localMessage.setTag("ORDER_CREATED");
localMessage.setMessage(JSON.toJSONString(order));
localMessage.setStatus(0);
localMessage.setRetryCount(0);
localMessage.setCreateTime(new Date());
localMessageMapper.insert(localMessage);
return true;
}
// 定时任务处理未发送的消息
@Scheduled(fixedDelay = 30000) // 每30秒执行一次
public void processPendingMessages() {
List<LocalMessage> pendingMessages = localMessageMapper.selectByStatus(0);
for (LocalMessage message : pendingMessages) {
try {
// 发送消息到MQ
rocketMQTemplate.syncSend(
message.getTopic() + ":" + message.getTag(),
message.getMessage()
);
// 更新消息状态
message.setStatus(1);
message.setUpdateTime(new Date());
localMessageMapper.update(message);
} catch (Exception e) {
// 增加重试次数
message.setRetryCount(message.getRetryCount() + 1);
message.setUpdateTime(new Date());
if (message.getRetryCount() > MAX_RETRY_COUNT) {
message.setStatus(2); // 标记为发送失败
}
localMessageMapper.update(message);
}
}
}
延迟消息处理
对于超时订单自动取消的场景,可以使用RocketMQ的延迟消息功能:
java
// 发送延迟消息
public void sendDelayCancelMessage(Order order) {
try {
// RocketMQ支持18个延迟级别: 1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
// level=3 表示延迟10秒
Message<String> delayMessage = MessageBuilder
.withPayload(JSON.toJSONString(order))
.setHeader("delayLevel", 3) // 延迟10秒
.build();
rocketMQTemplate.send("order-topic:ORDER_DELAY_CANCELLED", delayMessage);
System.out.println("延迟取消订单消息已发送: " + order.getOrderId());
} catch (Exception e) {
System.err.println("发送延迟取消订单消息失败: " + e.getMessage());
}
}
高级篇:消息模式与最佳实践
1. 消息分类策略
使用不同的Tag区分消息类型,让消费者可以灵活订阅:
java
// 多标签订阅
selectorExpression = "ORDER_LOG || ORDER_CREATED || ORDER_UPDATED"
2. 消息处理模式
同步发送
java
rocketMQTemplate.syncSend("topic:tag", message);
适用于重要消息,需要确保发送成功。
异步发送
java
rocketMQTemplate.asyncSend("topic:tag", message, new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
// 发送成功回调
}
@Override
public void onException(Throwable e) {
// 发送失败回调
}
});
适用于需要知道发送结果但又不想阻塞主线程的场景。
单向发送
java
rocketMQTemplate.sendOneWay("topic:tag", message);
适用于日志、通知等可容忍少量消息丢失的场景。
3. 异常处理最佳实践
java
try {
rocketMQTemplate.syncSend("order-topic:ORDER_CREATED", message);
} catch (Exception e) {
// 记录错误日志
log.error("消息发送失败", e);
// 根据业务需求决定是否重试
// 可以记录到本地消息表,由定时任务重试
}
4. 消费者幂等性处理
java
@Component
@RocketMQMessageListener(topic = "order-topic", consumerGroup = "order-created-consumer-group")
public class OrderCreatedConsumer implements RocketMQListener<String> {
@Autowired
private OrderService orderService;
@Override
public void onMessage(String message) {
Order order = JSON.parseObject(message, Order.class);
// 幂等性检查:检查订单是否已处理过
if (orderService.isOrderProcessed(order.getOrderId())) {
System.out.println("订单已处理过,跳过: " + order.getOrderId());
return;
}
// 处理订单
orderService.processOrder(order);
}
}
总结
通过这个完整的示例,我们学习了:
基础知识
- 消息队列的基本概念和作用
- RocketMQ的核心组件和工作原理
- Spring集成RocketMQ的方法
实践技能
- 消息的发送和接收
- 多消费者订阅和处理
- 延迟消息的使用
- 消息可靠性保障机制
高级技巧
- 本地消息表模式实现最终一致性
- 不同发送模式的选择和使用
- 消费者幂等性处理
- 异常处理和重试机制
项目优势
- 解耦服务:服务间通过消息进行通信,降低耦合度
- 异步处理:提高系统响应速度和吞吐量
- 可靠性:通过多种机制确保消息不丢失
- 可扩展性:易于添加新的业务消费者
- 灵活性:支持多种消息模式和处理策略
这种基于消息队列的微服务架构设计,能够很好地应对复杂的业务场景,是构建高可用、可扩展分布式系统的有效方案。希望这个完整的教程能帮助你更好地掌握RocketMQ在实际项目中的应用!