RocketMq重试机制

生产者重试

我们接着上一篇psringboot集成rocketmq修改发送消息代码,如下

 # SendResult sendResult = defaultMQProducer.send(message);
 SendResult sendResult = defaultMQProducer.send(message,5);

我们设置成5毫秒,然后看测试结果

org.apache.rocketmq.remoting.exception.RemotingTooMuchRequestException: sendDefaultImpl call timeout
	at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendDefaultImpl(DefaultMQProducerImpl.java:635)
	at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.send(DefaultMQProducerImpl.java:1280)
	at org.apache.rocketmq.client.producer.DefaultMQProducer.send(DefaultMQProducer.java:301)
	at com.wtc.util.SendMsgUtil.sendMsg(SendMsgUtil.java:31)
	at com.wtc.controller.TestController.sendRocketMq(TestController.java:89)
	at com.wtc.controller.TestController$$FastClassBySpringCGLIB$$b0d4440b.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
	......

然后我们看源码

private SendResult sendDefaultImpl(
        Message msg, final CommunicationMode communicationMode, final SendCallback sendCallback, final long timeout ) throws MQClientException, RemotingException, MQBrokerException, InterruptedException 
        {
        this.makeSureStateOK();
        Validators.checkMessage(msg, this.defaultMQProducer);

        final long invokeID = random.nextLong();
        long beginTimestampFirst = System.currentTimeMillis();
        long beginTimestampPrev = beginTimestampFirst;
        long endTimestamp = beginTimestampFirst;
        TopicPublishInfo topicPublishInfo = this.tryToFindTopicPublishInfo(msg.getTopic());
        if (topicPublishInfo != null && topicPublishInfo.ok()) {
            boolean callTimeout = false;
            MessageQueue mq = null;
            Exception exception = null;
            SendResult sendResult = null;
            #注意这里,我们使用的是异步发送消息,然后通过运算,我们得到 timesTotal = 3
            int timesTotal = communicationMode == CommunicationMode.SYNC ? 1 + this.defaultMQProducer.getRetryTimesWhenSendFailed() : 1;
            int times = 0;
            String[] brokersSent = new String[timesTotal];
            for (; times < timesTotal; times++) {
                String lastBrokerName = null == mq ? null : mq.getBrokerName();
                MessageQueue mqSelected = this.selectOneMessageQueue(topicPublishInfo, lastBrokerName);
                if (mqSelected != null) {
                    mq = mqSelected;
                    brokersSent[times] = mq.getBrokerName();
                    try {
                        beginTimestampPrev = System.currentTimeMillis();
                        long costTime = beginTimestampPrev - beginTimestampFirst;
                        #我们这里timeout 等于5,就是我们刚设置的时间,没有网络延迟,这块一般不走if,如果走if,就直接跳出for循环,然后报异常
                        if (timeout < costTime) {
                            callTimeout = true;
                            break;
                        }
                        #如果不走if,我们来到这个方法
                       sendResult = this.sendKernelImpl(msg, mq, communicationMode, sendCallback, topicPublishInfo, timeout - costTime);
		....

sendKernelImpl,接下来我们看这个源码,发现最后又是直接throw new RemotingTooMuchRequestException(“sendMessage call timeout”);
MQClientAPIImpl类

private SendResult sendKernelImpl(final Message msg,
                                      final MessageQueue mq,
                                      final CommunicationMode communicationMode,
                                      final SendCallback sendCallback,
                                      final TopicPublishInfo topicPublishInfo,
                                      final long timeout) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
        long beginStartTime = System.currentTimeMillis();
    	...
                    case SYNC:
                    #当你设置的超时时间越短,就会出现这个超时错误
                        long costTimeSync = System.currentTimeMillis() - beginStartTime;
                        if (timeout < costTimeSync) {
                            throw new RemotingTooMuchRequestException("sendKernelImpl call timeout");
                        }
                   ...
    }

通过源码很明显可以看出以下几点,对于生产者发送消息失败而言

  • 如果是异步发送 那么重试次数只有1次
  • 超时异常也是不会再去重试
  • 如果发生重试是在一个for 循环里去重试,所以它是立即重试而不是隔一段时间去重试

消费端重试

我们修改代码,把监听器返回ConsumeConcurrentlyStatus.CONSUME_SUCCESS修改成ConsumeConcurrentlyStatus.RECONSUME_LATER,然后在加上一个变量,看重试次数

@Override
    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
        msgs.forEach(messageExt -> {
            msgCustomerService.handlerMsg(messageExt);
        });
        i ++ ;
        logger.info("i = " + i);
        return ConsumeConcurrentlyStatus.RECONSUME_LATER;
    }

结果:

2020-04-10 16:14:08.337  INFO 10468 --- [MessageThread_1] c.w.m.m.impl.MsgCustomerServiceImpl      : tag_account--> 消息:6aedbd70-44a2-4220-a873-a3b71bf4b41f
2020-04-10 16:14:08.338  INFO 10468 --- [MessageThread_1] com.wtc.mqmsg.reviceMsg.MsgListener      : i = 1
2020-04-10 16:14:19.232  INFO 10468 --- [MessageThread_2] c.w.m.m.impl.MsgCustomerServiceImpl      : tag_account--> 消息:6aedbd70-44a2-4220-a873-a3b71bf4b41f
2020-04-10 16:14:19.232  INFO 10468 --- [MessageThread_2] com.wtc.mqmsg.reviceMsg.MsgListener      : i = 2
2020-04-10 16:14:49.237  INFO 10468 --- [MessageThread_3] c.w.m.m.impl.MsgCustomerServiceImpl      : tag_account--> 消息:6aedbd70-44a2-4220-a873-a3b71bf4b41f
2020-04-10 16:14:49.237  INFO 10468 --- [MessageThread_3] com.wtc.mqmsg.reviceMsg.MsgListener      : i = 3
2020-04-10 16:15:49.243  INFO 10468 --- [MessageThread_4] c.w.m.m.impl.MsgCustomerServiceImpl      : tag_account--> 消息:6aedbd70-44a2-4220-a873-a3b71bf4b41f
2020-04-10 16:15:49.244  INFO 10468 --- [MessageThread_4] com.wtc.mqmsg.reviceMsg.MsgListener      : i = 4
...

说明:

这里的超时异常并非真正意义上的超时,它指的是指获取消息后,因为某种原因没有给RocketMQ返回消费的状态,即没有return ConsumeConcurrentlyStatus.CONSUME_SUCCESS

  • Consumer是有一定时间间隔的。它照1S,5S,10S,30S,1M,2M····2H进行重试,默认是16次
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RocketMQ重试队列是指在RocketMQ消息中间件中,当某个消费者无法成功消费一条消息时,RocketMQ会对该消息进行重试,并将其投递到一个特殊的队列中,该队列被称为重试队列。 在RocketMQ的客户端源码中,可以看到对重试机制的实现,通过设置最大重试次数来控制消息的重试行为。在DefaultMQPushConsumerImpl.java文件中,可以找到获取最大重试次数的方法getMaxReconsumeTimes(),默认的最大重试次数是16,当达到最大重试次数后,RocketMQ会将消息投递至死信队列。 所以,当消息无法被成功消费时,RocketMQ会将其放入重试队列,进行最大重试次数的尝试。如果仍然无法消费成功,则消息会被投递到死信队列中。对于死信队列中的消息,我们需要关注并进行人工的业务补偿操作。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [springboot Rabbit死信队列实现,rocketMq重试消息实现](https://download.csdn.net/download/zhengjie01/11041887)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [关于RocketMQ之消息重试简述](https://blog.csdn.net/ysds20211402/article/details/124569910)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值