RocketMQ顺序消息实现

分区顺序消息:

  1. 生产者 :需要确保消息按照某个特定的键(如订单 ID)发送到同一个队列。
  2. 消费者 :允许消费者使用多线程,但每个分区的消息必须由单个线程处理。

分区顺序消息生产者代码示例:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class OrderTestRunner implements CommandLineRunner {

    @Autowired
    private OrderProducer orderProducer;

    @Override
    public void run(String... args) throws Exception {
        // 模拟发送多个订单消息
        String orderId = "order-001";
        orderProducer.sendSequentialMessage(orderId, "Step 1: Create Order");
        orderProducer.sendSequentialMessage(orderId, "Step 2: Pay Order");
        orderProducer.sendSequentialMessage(orderId, "Step 3: Deliver Order");

        // 可以尝试发送另一个订单的消息
        String anotherOrderId = "order-002";
        orderProducer.sendSequentialMessage(anotherOrderId, "Step 1: Create Another Order");
        orderProducer.sendSequentialMessage(anotherOrderId, "Step 2: Pay Another Order");
    }
}

全局顺序消息:

  1. 生产者 :需要将所有消息发送到同一个队列。
  2. 消费者 :需要以单线程的方式消费该队列的消息。

全局顺序消息生产者代码示例:

import org.apache.rocketmq.client.producer.MessageQueueSelector;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class GlobalOrderProducer {

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    /**
     * 发送消息到默认的第一个队列,并使用回调记录日志
     *
     * @param topic       主题名称
     * @param messageBody 消息内容
     */
    public void sendToFirstQueue(String topic, String messageBody) {
        try {
            // 创建消息对象
            Message message = new Message(topic, null, null, messageBody.getBytes());

            // 使用 MessageQueueSelector 选择第一个队列并发送消息
            rocketMQTemplate.getProducer().send(
                message,
                (MessageQueueSelector) (mqs, msg, arg) -> {
                    if (mqs == null || mqs.isEmpty()) {
                        throw new IllegalStateException("No available queues for topic: " + topic);
                    }
                    // 始终选择第一个队列
                    return mqs.get(0);
                },
                null, // 无额外参数
                new SendCallback() {
                    @Override
                    public void onSuccess(SendResult sendResult) {
                        // 成功回调:记录日志
                        System.out.println("Message sent successfully: " + messageBody + ", Result: " + sendResult);
                    }

                    @Override
                    public void onException(Throwable throwable) {
                        // 异常回调:记录日志
                        System.err.println("Failed to send message: " + messageBody);
                        throwable.printStackTrace();
                    }
                }
            );

        } catch (Exception e) {
            // 异常处理
            System.err.println("Error occurred while sending message: " + messageBody);
            e.printStackTrace();
        }
    }
}

总结:

生产者:

  • 分区顺序(局部顺序)消息生产, 每条消息必须指定一个键, 同一个键内的子消息保持顺序
  • 全局顺序消息生产, 必须指定唯一的分区(MessageQueue), 发送消息

生产者:

  • 分区顺序消息消费, 可以多队列(MessageQueue)消费, 单线程消费
  • 全局顺序消息消费, 必须要保证单队列(MessageQueue), 单线程消费
posted @ 2024-11-20 15:24  Journey&Flower  阅读(19)  评论(0)    收藏  举报