当发送拉取消息在Broker返回响应消息之后调用NettyRemotingAbstract.processMessageReceived(ChannelHandlerContext ctx, RemotingCommand msg)方法,大致逻辑如下:
1、根据返回的响应对象RemotingCommand的opaque(请求序列号)从NettyRemotingAbstract.responseTable: ConcurrentHashMap
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方法的等待;
该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
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
A)遍历List
B)更新ProcessQueue.msgCount变量,记录消息个数;
C)经过第A步处理之后,若msgTreeMap变量不是空并且ProcessQueue.consuming为false(初始化为false)则置consuming为true(在该msgTreeMap变量消费完之后再置为false)、置临时变量dispatchToConsume为true;否则置临时变量dispatchToConsume为false表示没有待消费的消息或者msgTreeMap变量中存入了数据还未消费完,在没有消费完之前不允许在此提交消费请求,在消费完msgTreeMap之后置consuming为false;
D)取List
E)返回临时变量dispatchToConsume值;
2.5)调用ConsumeMessageService.submitConsumeRequest(List
A)若是 顺序消费 ,则调用ConsumeMessageOrderlyService. submitConsumeRequest(List
B)若是 并发消费 ,则调用ConsumeMessageConcurrentlyService.submitConsumeRequest(List
2.6)检查拉取消息的间隔时间(DefaultMQPushConsumer.pullInterval,默认为0),若大于0,则调用DefaultMQPushConsumerImpl. executePullRequestLater方法,在间隔时间之后再将PullRequest对象放入PullMessageService线程的pullRequestQueue队列中;若等于0(表示立即再次进行拉取消息),则调用DefaultMQPushConsumerImpl. executePullRequestImmediately方法立即继续下一次拉取消息,从而形成一个循环不间断地拉取消息的过程;
3、若PullResult.status=NO_NEW_MSG或者NO_MATCHED_MSG时:
3.1)取PullResult.nextBeginOffset的值(Broker返回的下一次消费进度的偏移值)赋值给PullRequest.nextOffset变量值;
3.2)更新消费进度offset。调用DefaultMQPushConsumerImpl.correctTagsOffset(PullRequest pullRequest)方法。若没有获取到消息(即ProcessQueue.msgCount等于0)则更新消息进度。对于LocalFileOffsetStore或RemoteBrokerOffsetStore类,均调用updateOffset(MessageQueue mq, long offset, boolean increaseOnly)方法,而且方法逻辑是一样的,以MessageQueue对象为key值从offsetTable:ConcurrentHashMap
3.3)调用DefaultMQPushConsumerImpl.executePullRequestImmediately方法立即继续下一次拉取;
4、若PullResult.status=OFFSET_ILLEGAL
4.1)取PullResult.nextBeginOffset的值(Broker返回的下一次消费进度的偏移值)赋值给PullRequest.nextOffset变量值;
4.2)设置PullRequest.processQueue.dropped等于true,将此该拉取请求作废;
4.3)创建一个匿名Runnable线程类,然后调用DefaultMQPushConsumerImpl.executeTaskLater(Runnable r, long timeDelay)方法将该线程类放入PullMessageService.scheduledExecutorService: ScheduledExecutorService调度线程池中,在10秒钟之后执行该匿名线程类;该匿名线程类的run方法逻辑如下:
A)调用OffsetStore.updateOffset(MessageQueue mq, long offset, boolean increaseOnly)方法更新更新消费进度offset;
B)调用OffsetStore.persist(MessageQueue mq)方法:对于广播模式下offsetStore初始化为LocalFileOffsetStore对象,该对象的persist方法没有处理逻辑;对于集群模式下offsetStore初始化为RemoteBrokerOffsetStore对象,该对象的persist方法中,首先以入参MessageQueue对象为key值从RemoteBrokerOffsetStore.offsetTable: ConcurrentHashMap
C)以PullRequest对象的messageQueue变量为参数调用RebalanceImpl.removeProcessQueue(MessageQueue mq)方法,在该方法中,首先从RebalanceImpl.processQueueTable: ConcurrentHashMap
二、 onException方法
在发送消息失败或者等待响应超时或者其他异常时回调该onException方法。在该方法中,调用DefaultMQPushConsumerImpl.executePullRequestLater方法,3秒钟之后再将该PullRequest请求重新放入PullMessageService线程的pullRequestQueue队列中;
对于顺序消费的三把锁:1)首先在ConsumeMessageOrderlyService类中定义了定时任务每隔20秒执行一次lockMQPeriodically()方法,获取该Consumer端在Broker端锁住的MessageQueue集合(即分布式锁),并将RebalanceImpl.processQueueTable:ConcurrentHashMap
在回调DefaultMQPushConsumerImpl.pullMessage方法中的内部类PullCallback.onSucess方法时,调用ConsumeMessageOrderlyService. submitConsumeRequest方法提交消费请求ConsumeRequest对象,该消费请求就是在ConsumeMessageOrderlyService类的内部定义了ConsumeRequest线程类,将此对象提交到ConsumeMessageOrderlyService类的线程池中,由该线程完成回调业务层定义的消费方法,该内部线程类的run方法逻辑如下:
1、检查ProcessQueue.dropped是否为true,若不是则直接返回;
2、对MessageQueue对象加锁,保证同一时间只允许一个线程使用该MessageQueue对象。调用ConsumeMessageOrderlyService.messageQueueLock. fetchLockObject(MessageQueue mq)获取锁对象。下面的处理逻辑均在获得该Object对象的互斥锁(synchronized)后进行处理;从而保证了在并发情况下一个MessageQueue对象只有一个线程使用,大致逻辑为:
2.1)根据MessageQueue对象从MessageQueueLock.mqLockTable: ConcurrentHashMap
2.2)若获取的Object对象为null,则创建一个Object对象,并保存到mqLockTable变量中,为了防止并发采用putIfAbsent方法存入该列表中,若已经有该MessageQueue对象,则返回已经存在的Object对象,若不为空,则返回该已存在的Object对象;
3、若消息模式是广播或者对应的ProcessQueue.locked等于true且锁的时间未过期(根据获取锁locked的时候设置的lastLockTimestamp值来判断),则初始化局部变量continueConsume=true,然后无限期的循环执行下面的逻辑,直到局部变量continueConsume=false为止或者跳出循环,便终止了该方法的执行,否则执行第4步操作;
3.1)执行for循环下面的逻辑,直到该continueConsume等于false或者直接跳出循环为止;
3.2)检查ProcessQueue.dropped是否为true,若不是则跳出循环;
3.3)若消息模式是集群并且ProcessQueue.locked不等于true(未锁住)或者锁住了但是锁已经超时,则调用ConsumeMessageOrderlyService.tryLockLaterAndReconsume(MessageQueue mq, ProcessQueue processQueue, long delayMills)方法,在该方法中初始化一个线程并将线程放入ConsumeMessageOrderlyService. scheduledExecutorService线程池中,然后跳出循环。该线程在延迟delayMills毫秒之后被执行,该线程的功能是获取MessageQueue队列的分布式锁,然后再调用ConsumeMessageOrderlyService.submitConsumeRequestLater (MessageQueue mq, ProcessQueue processQueue, long delayMills)方法。
3.4)若该循环从开始到现在连续执行的时间已经超过的最大连续执行值(由property属性中的"rocketmq.client.maxTimeConsumeContinuously"设定,默认为60秒),则调用ConsumeMessageOrderlyService. submitConsumeRequestLater(MessageQueue mq, ProcessQueue processQueue, long delayMills)方法,其中delayMills=100,表示在100毫秒之后再调用ConsumeMessageOrderlyService.submitConsumeRequest(List
3.4)获取一次批量消费的消息个数batchSize(由参数DefaultMQPushConsumer.consumeMessageBatchMaxSize指定,默认为1),然后调用ProcessQueue.takeMessags(int batchSize),在该方法中,从ProcessQueue.msgTreeMap变量中获取batchSize个数的List
3.5)检查返回的List
3.6)检查DefaultMQPushConsumerImpl.consumeMessageHookList: ArrayList
3.7)执行业务层定义的消费消息的业务逻辑并返回消费结果。先调用ProcessQueue. lockConsume:ReentrantLock变量的lock方法获取锁(目的是防止在消费的过程中,被其他线程将此消费队列解锁了,从而引起并发消费的问题),然后调用ConsumeMessageOrderlyService. messageListener:MessageListenerOrderly类的consumeMessage方法,保证同一个ProcessQueue在同一时间只能有一个线程调用consumeMessage方法,由应用层实现该MessageListenerOrderly接口的consumeMessage方法,执行完成之后调用调用ProcessQueue. lockConsume:ReentrantLock变量的unlock方法释放锁;
3.8)当consumeMessage方法的返回值status为空时,将结果状态status赋值为ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
3.9)检查DefaultMQPushConsumerImpl.consumeMessageHookList: ArrayList
.registerConsumeMessageHook(ConsumeMessageHook hook)方法设置;
3.10)处理回调方法consumeMessage的消费结果,并将消费结果赋值给变量continueConsume。调用ConsumeMessageOrderlyService. processConsumeResult(List
3.11)继续从第3.1步开始遍历,不间断地从ProcessQueue对象的List
4、检查ProcessQueue.dropped是否为true,若不为true则直接返回,否则调用ConsumeMessageOrderlyService.tryLockLaterAndReconsume (MessageQueue mq, ProcessQueue processQueue, long delayMills)方法,其中delayMills=100,即在100毫秒之后重新获取锁后再次进行消费;
以回调应用层定义的ConsumeMessageOrderlyService.consumeMessage方法的返回处理结果为参数调用ConsumeMessageOrderlyService. processConsumeResult(List
1、若ConsumeOrderlyContext.autoCommit为true(默认为true,可以在应用层的实现类MessageListenerOrderly的consumeMessage方法中设置该值);根据consumeMessage方法返回的不同结果执行不同的逻辑:
1.1)若status等于SUCCESS,则调用ProcessQueue.commit()方法,大致逻辑如下:
A)获取ProcessQueue.lockTreeMap:ReentrantReadWriteLock锁对该方法的整个处理逻辑加锁;
B)从msgTreeMapTemp(在从msgTreeMap中获取消息并消费时存入的)中获取最后一个元素的key值,即最大的offset;
C)将ProcessQueue.msgCount值减去临时变量msgTreeMapTemp中的个数,即表示剩下的未消费的消息个数;
D)清空临时变量msgTreeMapTemp列表的值;
E)返回最大的offset+1的值并赋值给局部变量commitOffset值用于更新消费进度之用;
F)然后调用ConsumerStatsManager.incConsumeOKTPS(String group, String topic, long msgs)方法进行消费统计;
1.2)若status等于SUSPEND_CURRENT_QUEUE_A_MOMENT,稍后重新消费。大致逻辑如下:
1.2.1)首先调用ProcessQueue.makeMessageToCosumeAgain (List
A)获取ProcessQueue.lockTreeMap:ReentrantReadWriteLock锁对该方法的整个处理逻辑加锁;
B)将List
1.2.2)然后调用ConsumeMessageOrderlyService. submitConsumeRequestLater(ProcessQueue processQueue, MessageQueue messageQueue, long suspendTimeMillis)方法,其中suspendTimeMillis由ConsumeOrderlyContext.suspendCurrentQueueTimeMillis变量在应用层的回调方法中设定,默认为1000,表示在1000毫秒之后调用ConsumeMessageOrderlyService.submitConsumeRequest(List
1.2.3)置局部变量continueConsume=false;
1.2.4)调用ConsumerStatsManager.IncConsumeFailedTPS方法进行消费统计;
2、若ConsumeOrderlyContext.autoCommit为false,根据consumeMessage方法返回的不同状态执行不同的逻辑:
2.1)若status等于SUCCESS,仅调用ConsumerStatsManager.incConsumeOKTPS(String group, String topic, long msgs)方法进行消费统计,并未调用ProcessQueue.commit()方法清理队列数据;
2.2)若status等于COMMIT时,才调用ProcessQueue.commit()方法,并返回消费最大的offset值并赋值给局部变量commitOffset值用于更新消费进度之用;
2.3)若status等于ROLLBACK,表示要重新消费,
A)首先调用ProcessQueue.rollback()方法,将msgTreeMapTemp变量中的内容全部重新放入msgTreeMap变量中,同时清理msgTreeMapTemp变量;
B)然后调用ConsumeMessageOrderlyService.submitConsumeRequestLater (ProcessQueue processQueue, MessageQueue messageQueue, long suspendTimeMillis)方法,其中suspendTimeMillis由ConsumeOrderlyContext. suspendCurrentQueueTimeMillis变量在应用层的回调方法中设定,默认为1000,表示在1000毫秒之后调用ConsumeMessageOrderlyService. submitConsumeRequest(List
C)置局部变量continueConsume=false;
2.4)若status等于SUSPEND_CURRENT_QUEUE_A_MOMENT,将msgTreeMapTemp变量中的内容全部重新放入msgTreeMap变量中,然后结束此轮消费(continueConsume=false),等待1秒之后再次提交消费,大致逻辑如下:
A)首先调用ProcessQueue.makeMessageToCosumeAgain(List
B)然后调用ConsumeMessageOrderlyService.submitConsumeRequestLater (ProcessQueue processQueue, MessageQueue messageQueue, long suspendTimeMillis)方法,其中suspendTimeMillis由ConsumeOrderlyContext. suspendCurrentQueueTimeMillis变量在应用层的回调方法中设定,默认为1000;
C)置局部变量continueConsume=false;
D)调用ConsumerStatsManager.IncConsumeFailedTPS方法进行消费统计;
3、若上面两步得到的commitOffset值大于0,则调用OffsetStore.updateOffset(MessageQueue mq, long offset, boolean increaseOnly)方法更新消费进度。对于LocalFileOffsetStore或RemoteBrokerOffsetStore类,该方法逻辑是一样的,以MessageQueue对象为key值从offsetTable: ConcurrentHashMap
4、返回continueConsume值;若该值为false则结束此轮消费。
在集群模式下,若ProcessQueue未锁或者锁已经超时,则调用ConsumeMessageOrderlyService.tryLockLaterAndReconsume(MessageQueue mq, ProcessQueue processQueue, long delayMills)方法从Broker重新获取锁之后再进行消费。
在该方法中初始化一个Runnable匿名线程,并在delayMills毫秒之后再执行该匿名线程,该匿名线程的run方法逻辑如下:
1、先调用ConsumeMessageOrderlyService.lockOneMQ(MessageQueue mq)方法获取MessageQueue队列的锁,向该MessageQueue对象的brokerName下面的主用Broker发送LOCK_BATCH_MQ请求码的请求消息,请求Broker将发送的MessageQueue对象锁住;若该请求的MessageQueue对象在Broker返回的锁住集合中,则锁住成功了;
2、调用ConsumeMessageOrderlyService.submitConsumeRequestLater(ProcessQueue processQueue, MessageQueue messageQueue, long suspendTimeMillis)方法,若锁住成功则suspendTimeMillis=10;若未锁住,则suspendTimeMillis=3000。
3、在ConsumeMessageOrderlyService.submitConsumeRequestLater方法中,初始化一个匿名的Runnable线程类,在suspendTimeMillis毫秒之后,执行该线程类,在该类的run方法中调用ConsumeMessageOrderlyService.submitConsumeRequest(List
在回调DefaultMQPushConsumerImpl.pullMessage方法中的内部类PullCallback.onSucess方法时,调用ConsumeMessageConcurrentlyService.submitConsumeRequest方法提交消费请求ConsumeRequest对象,该消费请求对象是在ConsumeMessageConcurrentlyService类的内部定义了ConsumeRequest线程类;将此对象提交到ConsumeMessageConcurrentlyService类的线程池中,由该线程完成回调业务层定义的消费方法,该内部线程类的run方法逻辑如下:
1、检查ProcessQueue.dropped是否为true,若不是则直接返回;
2、检查DefaultMQPushConsumerImpl.consumeMessageHookList: ArrayList
3、遍历List
4、调用ConsumeMessageConcurrentlyService.messageListener: MessageListenerConcurrently的consumeMessage方法;该messageListener变量是由DefaultMQPushConsumer.registerMessageListener (MessageListener messageListener)方法在业务层设置的,对于并发消费,则在业务层就要实现MessageListenerConcurrently接口的consumeMessage方法;该回调方法consumeMessage返回ConsumeConcurrentlyStatus. CONSUME_SUCCESS或者ConsumeConcurrentlyStatus.RECONSUME_LATER值;
5、检查上一部分返回值status,若为null,则置status等于ConsumeConcurrentlyStatus.RECONSUME_LATER;
5、检查DefaultMQPushConsumerImpl.consumeMessageHookList: ArrayList
6、处理消费失败的消息。对于回调方法consumeMessage的执行结果为失败的消息,以内部Producer的名义重发到Broker端用于重试消费。若ProcessQueue.dropped为false,调用ConsumeMessageConcurrentlyService. processConsumeResult(ConsumeConcurrentlyStatus status, ConsumeConcurrentlyContext context, ConsumeRequest consumeRequest)方法,该方法的大致逻辑如下:
6.1)由于ConsumeConcurrentlyContext.ackIndex初始的默认值为Integer.MAX_VALUE,表示消费成功的个数,该变量值可由业务层在执行回调方法失败之后重新设定;若status等于CONSUME_SUCCESS则判断ackIndex是否大于ConsumeRequest中MessageExt列表的个数,如果大于则表示该列表的消息全部消费成功,则将ackIndex置为List
6.2)若此消息模式为广播(DefaultMQPushConsumer.messageModel设置),则从消费失败的列表位置(ackIndex+1)开始遍历List
6.3)若此消息模式为集群(DefaultMQPushConsumer.messageModel设置),则从消费失败的列表位置(ackIndex+1)开始遍历List
6.4)检查上一步的临时列表msgBackFailedList是否为空,若不为空则说明有发送重试消息失败的,则首先从ConsumeRequest.msgs: List
6.5)将消费成功了的消息(ConsumeRequest.msgs列表中剩下的)从 ProcessQueue.msgTreeMap列表中删除,同时更新 ProcessQueue.msgCount并返回该列表中第一个MessageExt元素的key值,即该元素的queueoffset值;
6.6)若上一步返回的queueoffset大于等于0,则调用 OffsetStore.updateOffset( MessageQueue mq, long offset, boolean increaseOnly)更新该消费队列的消费进度;
在业务层消费消息失败并且是集群模式下,会调用ConsumeMessageConcurrentlyService.sendMessageBack(MessageExt msg, ConsumeConcurrentlyContext context)方法。在该方法中调用DefaultMQPushConsumerImpl.sendMessageBack(MessageExt msg,int delayLevel, String brokerName)方法,大致逻辑如下:
1、根据brokerName获取Broker地址;
2、调用MQClientAPIImpl.consumerSendMessageBack(String addr, MessageExt msg, String consumerGroup, int delayLevel, long timeoutMillis)方法,其中delayLevel参数由ConsumeConcurrentlyContext. delayLevelWhenNextConsume可在业务层的回调方法中设置,默认为0(表示由服务器根据重试次数自动叠加);构建ConsumerSendMsgBackRequestHeader对象,其中该对象的offset等于MessageExt.commitLogOffset、originTopic等于MessageExt.topic、originMsgId等于MessageExt.msgId;然后发送CONSUMER_SEND_MSG_BACK请求码的信息给Broker(详见3.1.17小节);
3、如果发送重试消息出现异常,则构建以%RETRY%+consumerGroup为topic值的新Message消息,构建过程如下:
3.1)从MessageExt消息的properties属性中获取"ORIGIN_MESSAGE_ID"属性值,若没有则以MessageExt消息的msgId来设置新Message信息的properties属性的ORIGIN_MESSAGE_ID属性值,若有该属性值则以该属性值来设置新Message信息的properties属性的ORIGIN_MESSAGE_ID属性值。保证同一个消息有多次发送失败能获取到真正消息的msgId;
3.2)将MessageExt消息的flag赋值给新Message信息的flag值;
3.3)将MessageExt消息的topic值存入新Message信息的properties属性中"RETRY_TOPIC"属性的值;
3.4)每次消费重试将MessageExt消息的properties属性中"RECONSUME_TIME"值加1;然后将该值再加3之后存入新Message信息的properties属性中"DELAY"属性的值;
4、调用在启动Consumer时创建的名为"CLIENT_INNER_PRODUCER"的DefaultMQProducer对象的send(Message msg)方法,以Consumer内部的消息Producer重发消息(该消息是消费失败且回传给Broker也失败的);