44-RocketMQ 核心原理与实战

3 阅读17分钟

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 的对比

特性RocketMQKafka
设计目标业务消息日志/流处理
消息可靠性极高(金融级别)
事务消息支持不支持(需要额外实现)
延迟消息支持(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 核心优势

  1. 高可靠:事务消息、消息轨迹、死信队列
  2. 高可用:主从复制、故障转移
  3. 丰富特性:延迟消息、顺序消息、消息过滤
  4. 金融级别:经历过双十一考验

使用场景

场景推荐消息类型
订单支付事务消息
订单超时取消延迟消息
订单状态流顺序消息
日志收集普通消息
配置推送广播消息

常见问题

问题原因解决方案
消息丢失Broker 故障多副本、同步刷盘
消息重复网络抖动重试消费者幂等
消息积压消费慢增加消费者、异步处理
顺序错乱多队列并发使用顺序消息

六、思考与练习

思考题

  1. 基础题:RocketMQ的NameServer与Kafka的Zookeeper在功能上有什么区别?为什么RocketMQ选择不使用Zookeeper?

  2. 进阶题:RocketMQ的事务消息如何保证本地事务与消息发送的原子性?事务回查机制的工作流程是什么?

  3. 实战题:对比RocketMQ、Kafka、RabbitMQ的事务消息支持能力,在分布式事务场景下应该如何选择?

编程练习

练习:使用Spring Boot集成RocketMQ实现一个完整的订单系统,包含:(1) 事务消息保证下单与库存扣减的一致性;(2) 延迟消息实现订单30分钟超时取消;(3) 顺序消息保证同一订单的状态变更有序;(4) SQL过滤实现不同消费者的消息分发。

章节关联

  • 前置章节:Kafka核心原理与实战、分布式事务详解
  • 后续章节:消息可靠性保证详解
  • 扩展阅读:RocketMQ官方文档、《RocketMQ技术内幕》

📝 下一章预告

无论使用哪种消息队列,消息的可靠性保障都是核心问题。下一章将深入分析消息丢失、重复、乱序的根本原因,并提供幂等设计、消息去重、补偿机制等完整的解决方案。


本章完


参考资料: