RocketMQ的顺序消息

        顺序消息是指消息的消费顺序和产生顺序相同,在有些业务逻辑下,必须保证顺序。比如订单的生成、付款、发货,这3个消息必须按顺序处理才行。顺序消息分为全局顺序消息和部分顺序消息,全局顺序消息指某个Topic下的所有消息都要保证顺序;部分顺序消息只要保证每一组消息被顺序消费即可,比如上面订单消息的例子,只要保证同一个订单ID的三个消息能按顺序消费即可。

1.全局顺序消息

        RocketMQ在默认情况下不保证顺序,比如创建一个Topic,默认八个写队列,八个读队列。这时候一条消息可能被写入任意一个队列里;在数据的读取过程中,可能有多个Consumer,每个Consumer也可能启动多个线程并行处理,所以消息被哪个Consumer消费,被消费的顺序和写入的顺序是否一致是不确定的。

        要保证全局顺序消息,需要先把Topic的读写队列数设置为一,然后Producer和Consumer的并发设置也要是一。简单来说,为了保证整个Topic的全局消息有序,只能消除所有的并发处理,各部分都设置成单线程处理。这时高并发、高吞吐量的功能完全用不上了。

        在实际应用中,更多的是像订单类消息那样,只需要部分有序即可。在这种情况下,我们经过合适的配置,依然可以利用RocketMQ高并发、高吞吐量的能力。

2.部分顺序消息

        要保证部分消息有序,需要发送端和消费端配合处理。在发送端,要做到把同一业务ID的消息发送到同一个Message Queue;在消费过程中,要做到从同一个Message Queue读取的消息不被并发处理,这样才能达到部分有序。

发送端使用MessageQueueSelector类来控制把消息发往哪个Message Queue,如代码清单6-1所示。

代码清单6-1 MessageQueueSelector示例

for (int i = 0; i < 100; i++) {
    int orderId = i;
    //Create a message instance, specifying topic, tag and message body.
    Message msg = new Message("OrderTopic8", tags, "KEY" + i,
        ("Hello RocketMQ " +orderId+"  "+ i).getBytes(RemotingHelper.DEFAULT_CHARSET));
    SendResult sendResult = Producer.send(msg, new MessageQueueSelector() {
        @Override
        public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
            System.out.println("queue selector mq nums:"+mqs.size());
            System.out.println("msg info:"+msg.toString());
            for(MessageQueue mq: mqs){
                System.out.println(mq.toString());
            }
            Integer id = (Integer) arg;
            int index = id % mqs.size();
            return mqs.get(index);
        }
    }, orderId);
    System.out.println(sendResult);
}

 

消费端通过使用MessageListenerOrderly类来解决单Message Queue的消息被并发处理的问题,如代码清单6-2所示。

代码清单6-2 MessageListenerOrderly示例

consumer.registerMessageListener(new MessageListenerOrderly() {
    AtomicLong consumeTimes = new AtomicLong(0);
    @Override
    public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs,
                                ConsumeOrderlyContext context) {
        System.out.printf(" Received New Messages: " + new String(msgs.get(0).getBody()) + "%n");
            return ConsumeOrderlyStatus.SUCCESS;
    }
});

 

        Consumer使用MessageListenerOrderly的时候,下面四个Consumer的设置依旧可以使用:setConsumeThreadMin、setConsumeThreadMax、setPull-BatchSize、setConsumeMessageBatchMaxSize。前两个参数设置Consumer的线程数,PullBatchSize指的是一次从Broker的一个Message Queue获取消息的最大数量,默认值是32,ConsumeMessageBatchMaxSize指的是这个Consumer的Executor(也就是调用MessageListener处理的地方)一次传入的消息数(List<MessageExt>msgs这个链表的最大长度),默认值是1。上述四个参数可以使用,说明MessageListenerOrderly并不是简单地禁止并发处理。在MessageListenerOrderly的实现中,为每个Consumer Queue加个锁,消费每个消息前,需要先获得这个消息对应的Consumer Queue所对应的锁,这样保证了同一时间,同一个Consumer Queue的消息不被并发消费,但不同Consumer Queue的消息可以并发处理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Hamilton_Huan

原创不易,结合业务原创更不易

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

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

打赏作者

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

抵扣说明:

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

余额充值