生产者重试
我们接着上一篇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次