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