MessageListenerOrderly vs MessageListenerConcurrently(只设置一个线程) 有啥区别

本文详细对比了RocketMQ中MessageListenerOrderly与MessageListenerConcurrently两种消息消费方式的区别,重点介绍了如何通过ConsumeMessageOrderlyService实现消息的有序消费,以及这两种消费方式在消费失败情况下的处理差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

MessageListenerOrderly  vs  MessageListenerConcurrently(只设置一个线程) 有啥区别


1.拉取broker消息存储在本地可能不同。转移到TreeMap msgTreeMap

如何在集群消费时保证消费的有序呢?
1.ConsumeMessageOrderlyService类的start()方法,如果是集群消费,则启动定时任务,定时向broker发送批量锁 住当前正在消费的队列集合的消息,具体是consumer端拿到正在消费的队列集合,发送锁住队列的消息至broker,broker端返回锁住成功的队 列集合。 
consumer收到后,设置是否锁住标志位。 
这里注意2个变量: 
consumer端的RebalanceImpl里的ConcurrentHashMap processQueueTable,是否锁住设置在ProcessQueue里。 
broker端的RebalanceLockManager里的ConcurrentHashMap* group *, ConcurrentHashMap> mqLockTable,这里维护着全局队列锁。

2.ConsumeMessageOrderlyService.ConsumeRequest的run方法是消费消息,这里还有个 MessageQueueLock messageQueueLock,维护当前consumer端的本地队列锁。保证当前只有一个线程能够进行消费。

3.拉到消息存入ProcessQueue,然后判断,本地是否获得锁,全局队列是否被锁住,然后从ProcessQueue里取出消息,用MessageListenerOrderly进行消费。 
拉到消息后调用ProcessQueue.putMessage(final List msgs) 存入,具体是存入TreeMap msgTreeMap。 
然后是调用ProcessQueue.takeMessags(final int batchSize)消费,具体是把msgTreeMap里消费过的消息,转移到TreeMap msgTreeMapTemp。

4.本地消费的事务控制,ConsumeOrderlyStatus.SUCCESS(提 交),ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT(挂起一会再消费),在此之前还有一个 变量ConsumeOrderlyContext context的setAutoCommit()是否自动提交。 
当SUSPEND_CURRENT_QUEUE_A_MOMENT时,autoCommit设置为true或者false没有区别,本质跟消费相反,把消息从msgTreeMapTemp转移回msgTreeMap,等待下次消费。

当SUCCESS时,autoCommit设置为true时比设置为false多做了2个动 作,consumeRequest.getProcessQueue().commit()和 this.defaultMQPushConsumerImpl.getOffsetStore().updateOffset(consumeRequest.getMessageQueue(), commitOffset, false); 
ProcessQueue.commit() :本质是删除msgTreeMapTemp里的消息,msgTreeMapTemp里的消息在上面消费时从msgTreeMap转移过来的。 
this.defaultMQPushConsumerImpl.getOffsetStore().updateOffset() :本质是把拉消息的偏移量更新到本地,然后定时更新到broker。

那么少了这2个动作会怎么样呢,随着消息的消费进行,msgTreeMapTemp里的消息堆积越来越多,消费消息的偏移量一直没有更新到broker导致consumer每次重新启动后都要从头开始重复消费。 
就算更新了offset到broker,那么msgTreeMapTemp里的消息堆积呢?不知道这算不算bug。 
所以,还是把autoCommit设置为true吧。


相关源码路口:
com.alibaba.rocketmq.client.impl.consumer.DefaultMQPushConsumerImpl.pullMessage(PullRequest)

com.alibaba.rocketmq.client.impl.consumer.ConsumeMessageOrderlyService.submitConsumeRequestLater(ProcessQueue, MessageQueue, long)

com.alibaba.rocketmq.client.impl.consumer.ConsumeMessageConcurrentlyService.submitConsumeRequestLater(List<MessageExt>, ProcessQueue, MessageQueue)


ConsumeMessageConcurrentlyService:

  class ConsumeRequest implements Runnable {
        private final List<MessageExt> msgs;
        private final ProcessQueue processQueue;
        private final MessageQueue messageQueue;


          //处理msgs.msgs是拉消息拉下来的arrayList
          status = listener.consumeMessage(msgs, context);


1.从拉取消息消费差不多
MessageListenerConcurrently 拉取到消息直接消费
MessageListenerOrderly  

顺序消费是直接从processQueue,里面取,processQueue里面做了临时存储
但是,并发消费没有做临时存储


2.消费失败考虑

如果是并发消费,消费失败,直接将失败信息发回borker
如果是顺序消费,好像不将消费失败的消息发回borker

相关源码:
com.alibaba.rocketmq.client.impl.consumer.ConsumeMessageOrderlyService.processConsumeResult(List<MessageExt>, ConsumeOrderlyStatus, ConsumeOrderlyContext, ConsumeRequest)

com.alibaba.rocketmq.client.impl.consumer.ConsumeMessageConcurrentlyService.processConsumeResult(ConsumeConcurrentlyStatus, ConsumeConcurrentlyContext, ConsumeRequest)


所以说:MessageListenerConcurrently(只设置一个线程),还是不能保证顺序消费


          
 

转载于:https://my.oschina.net/xiaominmin/blog/1795308

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值