RocketMQ——Consumer篇:PUSH模式下消费消息(顺序和并发两种)

本文详细介绍了RocketMQ Consumer的PUSH模式下顺序消费和并发消费的实现原理,包括接受Broker响应、消费消息回调、顺序与并发消费流程、消息过滤、消费失败处理及重试机制。内容深入解析了拉取消息的处理过程,以及在不同消费模式下如何回调业务层定义的消费方法和处理消费结果。
摘要由CSDN通过智能技术生成

1 接受并处理Broker返回的响应消息

当发送拉取消息在Broker返回响应消息之后调用NettyRemotingAbstract.processMessageReceived(ChannelHandlerContext ctx, RemotingCommand msg)方法,大致逻辑如下:

1、根据返回的响应对象RemotingCommand的opaque(请求序列号)从NettyRemotingAbstract.responseTable: ConcurrentHashMap<Integer /* opaque */, ResponseFuture>变量中获取ResponseFuture对象(在发送之前存入的该对象);

2、若该ResponseFuture对象为null,则啥也不干,就打警告日志;

3、若该ResponseFuture对象不为null,则将响应对象RemotingCommand赋值给ResponseFuture.responseCommand变量;

4、若ResponseFuture.invokeCallback:InvokeCallback变量不为空(在异步发送的情况下该变量不为空),则首先获取NettyRemotingClient.publicExecutor线程池,若存在该线程池则初始化Runnable匿名线程,将该匿名线程提交到线程池中;该匿名线程的run方法主要是调用ResponseFuture.executeInvokeCallback()方法;若没有该线程池则直接在主线程中调用ResponseFuture.executeInvokeCallback()方法;在executeInvokeCallback方法中,先确保ResponseFuture.executeCallbackOnlyOnce的值为false并且成功更新为true,则再执行InvokeCallback.operationComplete(ResponseFuture)方法,在该方法内部调用回调类PullCallback对象的onSuccess方法;由于executeCallbackOnlyOnce在初始化时为false,若更新失败说明该回调方法已经执行过了,故不在执行。

5、若ResponseFuture.invokeCallback:InvokeCallback变量为空(在同步方式拉取消息的情况下),则调用ResponseFuture.putResponse (RemotingCommand responseCommand)方法首先将响应对象RemotingCommand赋值给ResponseFuture.responseCommand变量,然后唤醒ResponseFuture.waitResponse方法的等待;

2 消费消息的回调类(PullCallback)

该PullCallback类是DefaultMQPushConsumerImpl.pullMessage (PullRequest pullRequest)方法中的匿名内部类,采用异步方式拉取消息时,在收到Broker的响应消息之后,回调该方法执行业务调用者的回调逻辑。

一、 onSucess方法

在收到响应消息之后,先回调InvokeCallback匿名类的operationComplete (ResponseFuture responseFuture)方法,在正常情况下会回调PullCallback类的onSucess方法,大致逻辑如下:

1、调用PullAPIWrapper.processPullResult(MessageQueue mq,PullResult

pullResult, SubscriptionData subscriptionData)方法处理拉取消息的返回对象PullResult,大致逻辑如下:

1.1)调用PullAPIWrapper.updatePullFromWhichNode(MessageQueue mq, long brokerId)方法用Broker返回的PullResultExt.suggestWhichBrokerId变量值更新PullAPIWrapper.pullFromWhichNodeTable:ConcurrentHashMap <MessageQueue,AtomicLong/* brokerId */>变量中当前拉取消息PullRequest.messageQueue对象对应的BrokerId。若以messageQueue为key值从pullFromWhichNodeTable中获取的BrokerId为空则将PullResultExt.suggestWhichBrokerId存入该列表中,否则更新该MessageQueue对应的value值为suggestWhichBrokerId;

1.2)若pullResult.status=FOUND,则继续下面的处理逻辑,否则设置PullResultExt.messageBinary=null并返回该PullResult对象;

1.3)对PullResultExt.messageBinary变量进行解码,得到MessageExt列表;

1.4) Consumer端消息过滤。若SubscriptionData.tagsSet集合(在5.5.1小节中拉取消息之前以topic获取的订阅关系数据)不为空并且SubscriptionData. classFilterMode为false(在初始化DefaultMQPushConsumer时可以设置这两个值),则遍历MessageExt列表,检查每个MessageExt对象的tags值(在commitlog数据的properties字段的"TAGS"属性值)是否在SubscriptionData.tagsSet集合中,只保留MessageExt.tags此tagsSet集合中的MessageExt对象,构成新的MessageExt列表,取名msgListFilterAgain;否则新的列表msgListFilterAgain等于上一步的MessageExt列表;

Consumer收到过滤后的消息后,同样也要执行在Broker端的操作,但是比对的是真实的Message Tag字符串,而不是hashCode。因为在Broker端为了节约空间,过滤规则是存储的HashCode,为了避免Hash冲突而受到错误消息,在Consumer端还进行一次具体过滤规则的过滤,进行过滤修正。

1.5)检查PullAPIWrapper.filterMessageHookList列表是否为空(可在应用层通过DefaultMQPullConsumerImpl.registerFilterMessageHook (FilterMessageHook hook)方法设置),若不为空则调用该列表中的每个FilterMessageHook对象的filterMessage方法;由应用层实现FilterMessageHook接口的filterMessage方法,可以在该方法中对消息再次过滤;

1.6)向NameServer发送GET_KV_CONFIG请求码获取NAMESPACE_PROJECT_CONFIG和本地IP下面的value值,赋值给projectGroupPrefix变量。若该值为空则将PullResult.minOffset和PullResult.maxOffset值设置到每个MessageExt对象的properties属性中,其中属性名称分别为"MIN_OFFSET"和"MAX_OFFSET";若不为空则除了将PullResult.minOffset和PullResult.maxOffset值设置到每个MessageExt对象的properties属性中之外,还从projectGroupPrefix变量值开头的topic中去掉projectGroupPrefix值部分,然后将新的topic设置到MessageQueue、SubscriptionData的topic以及每个MessageExt对象的topic变量;

1.7)将新组建的MessageExt列表msgListFilterAgain赋值给PullResult.msgFoundList变量;

1.8)设置PullResultExt.messageBinary=null,并返回该PullResult对象;

2、下面根据PullResult.status变量的值执行不同的业务逻辑,若PullResult.status=FOUND,大致逻辑如下:

2.1)该PullRequest对象的nextOffset变量值表示本次消费的开始偏移量,赋值给临时变量prevRequestOffset;

2.2)取PullResult.nextBeginOffset的值(Broker返回的下一次消费进度的偏移值)赋值给PullRequest.nextOffset变量值;

2.3)若PullResult.MsgFoundList列表为空,则调用DefaultMQPushConsumerImpl.executePullRequestImmediately(PullRequest pullRequest)方法将该拉取请求对象PullRequest重新延迟放入PullMessageService线程的pullRequestQueue队列中,然后跳出该onSucess方法;否则继续下面的逻辑;

2.4)调用该PullRequest.ProcessQueue对象的putMessage(List<MessageExt> msgs)方法,将MessageExt列表存入ProcessQueue.msgTreeMap:TreeMap<Long, MessageExt>变量中,放入此变量的目的是:第一在顺序消费时从该变量列表中取消息进行消费,第二可以用此变量中的消息做流控;大致逻辑如下:

A)遍历List<MessageExt>列表,以每个MessageExt对象的queueOffset值为key值,将MessageExt对象存入msgTreeMap:TreeMap<Long, MessageExt>变量中;该变量类型根据key值大小排序;

B)更新ProcessQueue.msgCount变量,记录消息个数;

C)经过第A步处理之后,若msgTreeMap变量不是空并且ProcessQueue.consuming为false(初始化为false)则置consuming为true(在该msgTreeMap变量消费完之后再置为false)、置临时变量dispatchToConsume为true;否则置临时变量dispatchToConsume为false表示没有待消费的消息或者msgTreeMap变量中存入了数据还未消费完,在没有消费完之前不允许在此提交消费请求,在消费完msgTreeMap之后置consuming为false;

D)取List<MessageExt>列表的最后一个MessageExt对象,该对象的properties属性中取MAX_OFFSET的值,减去该MessageExt对象的queueOf

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值