RocketMQ微服务架构实践:从入门到精通完整指南

通过一个完整的电商场景,从基础到进阶全面掌握在Spring中使用RocketMQ的方法。这是一个循序渐进的教程,旨在帮助你系统性地掌握RocketMQ在微服务架构中的应用。

demo可以通过https://gitee.com/tanxiaomi/interview-demo/blob/master/Interview-demo.rar下载

目录

  1. 场景设定:小明的水果店
  2. 基础篇:消息队列入门
  3. 项目结构详解
  4. 核心流程实现
  5. 进阶篇:消息可靠性保障
  6. 高级篇:消息模式与最佳实践
  7. 总结

场景设定:小明的水果店

想象一下,小明开了一家水果店,有收银台和库存管理系统。当顾客在收银台付款后,系统需要做几件事:

  1. 创建订单记录
  2. 扣减库存
  3. 记录操作日志
  4. 处理高价值订单
  5. 自动取消超时未支付订单

如果这些操作都串行执行,顾客可能要等很久。这时候,RocketMQ就派上用场了!

基础篇:消息队列入门

什么是消息队列?

消息队列是一种在分布式系统中传递消息的通信方式。它就像一个"邮局",发送方把消息投递到邮局,接收方从邮局取走邮件。

RocketMQ核心概念

  • Producer(生产者):发送消息的一方(如收银台服务)
  • Consumer(消费者):接收消息的一方(如库存服务)
  • Topic(主题):消息的分类,如"订单相关"
  • Tag(标签):更细粒度的分类,如"订单创建"、"订单取消"
  • Message(消息):具体的数据内容
  • Consumer Group(消费组) :消息负载均衡和容错机制,这里有疑惑可以看我另外一篇博客消费组详细介绍

为什么需要消息队列?

  1. 解耦:服务之间不需要直接调用
  2. 异步:提升用户体验
  3. 削峰:应对流量高峰
  4. 可靠性:确保消息不丢失

项目结构详解

复制代码
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);
    }
}

总结

通过这个完整的示例,我们学习了:

基础知识

  1. 消息队列的基本概念和作用
  2. RocketMQ的核心组件和工作原理
  3. Spring集成RocketMQ的方法

实践技能

  1. 消息的发送和接收
  2. 多消费者订阅和处理
  3. 延迟消息的使用
  4. 消息可靠性保障机制

高级技巧

  1. 本地消息表模式实现最终一致性
  2. 不同发送模式的选择和使用
  3. 消费者幂等性处理
  4. 异常处理和重试机制

项目优势

  1. 解耦服务:服务间通过消息进行通信,降低耦合度
  2. 异步处理:提高系统响应速度和吞吐量
  3. 可靠性:通过多种机制确保消息不丢失
  4. 可扩展性:易于添加新的业务消费者
  5. 灵活性:支持多种消息模式和处理策略

这种基于消息队列的微服务架构设计,能够很好地应对复杂的业务场景,是构建高可用、可扩展分布式系统的有效方案。希望这个完整的教程能帮助你更好地掌握RocketMQ在实际项目中的应用!

相关推荐
羑悻的小杀马特2 小时前
openGauss 数据库快速上手评测:从 Docker 安装到SQL 实战
数据库·sql·docker·opengauss
德迅云安全-小潘2 小时前
SQL:从数据基石到安全前线的双重审视
数据库·sql·安全
Databend2 小时前
Databend SQL nom Parser 性能优化
数据库
艾斯比的日常3 小时前
Redis 大 Key 深度解析:危害、检测与治理实践
数据库·redis·缓存
R.lin3 小时前
MySQL核心知识点梳理
数据库·mysql
百***06943 小时前
SQL JOIN:内连接、外连接和交叉连接(代码+案例)
数据库·sql·oracle
大数据魔法师3 小时前
MySQL(六) - 视图管理
数据库·mysql
Hello.Reader4 小时前
从 WAL 到 Fluss->Flink CDC Postgres Connector 端到端同步实战
数据库·flink
千桐科技4 小时前
数据库设计最佳实践:我们团队沉淀下来的规范
数据库·代码规范·设计