RocketMQ如何实现顺序消费

文章讲述了RocketMQ默认情况下由于多线程消费导致的顺序问题,并提出了通过取模运算将消息发送到同一队列以保证顺序消费的解决方案。在生产者端,使用MessageQueueSelector根据订单号选择队列;在消费者端,从第一个offset开始消费并使用有序消息监听器确保顺序处理。
摘要由CSDN通过智能技术生成

1、为什么会产生这个问题?

默认MQ没有实现顺序消费,因为RocketMQ1个topic中有四个队列,且消费是多线程进行消费的。即使队列能够保证先进先出,但是由于多线程CPU执行的随机性,我们无法保证消费的顺序,从而导致了消费的乱序。

2、解决方法

RocketMQ提供了解决办法,我们只需要将需要顺序执行的消息通过取模运算的方式放到同一个队列中。在生产者和消费者的代码中选择合适的方法(代码如下)。

3、代码实现

//生产者代码
public class Producer {
    public static void main(String[] args) throws Exception{
        //定义一个生产对象
        DefaultMQProducer producer = new DefaultMQProducer("orderlyProducerGroup");
        //连接nameServer
        producer.setNamesrvAddr("10.0.0.129:9876");
        //启动生产者
        producer.start();
        //设置消息发送的目的地Topic
        String topic = "orderTopic";
        List<OrderStep> orderSteps = OrderUtil.buildOrders();


        MessageQueueSelector selector =  new MessageQueueSelector() {
            @Override
            public MessageQueue select(List<MessageQueue> list, Message message, Object o) {
                System.out.println("队列的个数是"+list.size());
                Long orderId = (Long) o;
                //根据订单号选取队列,订单号相同的队列相同,返回list中的队列
                int index = (int) (orderId%list.size());//这样返回的值就是0 1 2 3
                return list.get(index);
            }
        };
        for (OrderStep step : orderSteps) {
            Message msg = new Message(topic, step.toString().getBytes(Charset.defaultCharset()));
            //指定消息选择器  (消息,队列选择器,顺序消费的参数【这里是订单号就是上面消费选择器里的Object o参数】)
            producer.send(msg,selector,step.getOrderId());
        }
        System.out.println("发送完毕");
        producer.shutdown();

    }
}
//消费者代码
public class Consumer {
    public static void main(String[] args) throws Exception {
        //定义同一个消息消费者
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("orderlyConsumerGroup");
        //设置NameServer地址
        consumer.setNamesrvAddr("10.0.0.129:9876");
        //设置订阅的主题
        consumer.subscribe("orderTopic","*");
        //从什么地方开始消费                           (为了实现顺序消费,从头开始消费)
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
        //设置消息的监听器
       consumer.setMessageListener(new MessageListenerOrderly() {
           @Override
           public ConsumeOrderlyStatus consumeMessage(List<MessageExt> list, ConsumeOrderlyContext consumeOrderlyContext) {
               for (MessageExt msg : list) {
                   System.out.println("当前线程:"+Thread.currentThread()+",队列ID:"+msg.getQueueId()+",消息内容"+new String(msg.getBody(),Charset.defaultCharset()));
               }
               return ConsumeOrderlyStatus.SUCCESS;
           }
       });
       //启动消费者
        consumer.start();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

谢少迪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值