这是我参与11月更文挑战的第16天,活动详情查看:2021最后一次更文挑战
前言
RocketMQ消息重试机制,主要从两个方面考虑, 1. 生产者消息失败重试 2. 消费者消息失败重试。
RocketMQ单机版本部署
主要是为了模拟一下,RoceketMQ消息重试的场景,我们可以搭建一个简单的单机版的RocketMQ。
1.下载RocketMQ
到Apache官网下载RocketMQ安装包,这里,我下载的RocketMQ的版本为4.5.2。在CentOS 8 命令行输入如下命令下载RocketMQ安装包
wget http://mirrors.tuna.tsinghua.edu.cn/apache/rocketmq/4.5.2/rocketmq-all-4.5.2-bin-release.zip
2.安装RocketMQ
- 解压
unzip rocketmq-all-4.5.2-bin-release.zip
生产者Producer重试
Producer端重试一般, Producer往MQ上发消息没有发送成功,比如网络原因导致生产者发送消息到MQ失败。 消息重试原则上可以保证消息发送成功以及不丢失,但是消息重新投递可能造成消费者重复消费,RocketMQ不保证幂等性,所以开发者如果有幂等性的要求,需要自行保证幂等
mq的重试的默认值:同步需要开启重试配置:retryAnotherBrokerWhenNotStoreOK = true,默认是不开启重试
消费者Consumer重试
在实际的项目开发过程种我们可能更多的会关注消费端重试机制。消费端重试一般分为两种,Exception 和 Timeout。
消费者Consumer一直重试问题
代码很简单RocketMQ消费者,每次消费一批消息,成功之后返回CONSUMER_SUCCESS,但是生产上有一笔mq消息处理异常,钉钉消息一直提醒,并没有受cnsumer失败重试机制,按照时间间隔重试,达到16次之后停止。
我们带着这个问题, 去看一下RocketMq源码中consumer的处理逻辑。
@Override
public ConsumeMessageDirectlyResult consumeMessageDirectly(MessageExt msg, String brokerName) {
ConsumeMessageDirectlyResult result = new ConsumeMessageDirectlyResult();
result.setOrder(false);
result.setAutoCommit(true);
List<MessageExt> msgs = new ArrayList<MessageExt>();
msgs.add(msg);
MessageQueue mq = new MessageQueue();
mq.setBrokerName(brokerName);
mq.setTopic(msg.getTopic());
mq.setQueueId(msg.getQueueId());
ConsumeConcurrentlyContext context = new ConsumeConcurrentlyContext(mq);
this.defaultMQPushConsumerImpl.resetRetryAndNamespace(msgs, this.consumerGroup);
final long beginTime = System.currentTimeMillis();
log.info("consumeMessageDirectly receive new message: {}", msg);
try {
ConsumeConcurrentlyStatus status = this.messageListener.consumeMessage(msgs, context);
if (status != null) {
switch (status) {
case CONSUME_SUCCESS:
result.setConsumeResult(CMResult.CR_SUCCESS);
break;
case RECONSUME_LATER:
result.setConsumeResult(CMResult.CR_LATER);
break;
default:
break;
}
} else {
result.setConsumeResult(CMResult.CR_RETURN_NULL);
}
} catch (Throwable e) {
result.setConsumeResult(CMResult.CR_THROW_EXCEPTION);
result.setRemark(RemotingHelper.exceptionSimpleDesc(e));
log.warn(String.format("consumeMessageDirectly exception: %s Group: %s Msgs: %s MQ: %s",
RemotingHelper.exceptionSimpleDesc(e),
ConsumeMessageConcurrentlyService.this.consumerGroup,
msgs,
mq), e);
}
result.setSpentTimeMills(System.currentTimeMillis() - beginTime);
log.info("consumeMessageDirectly Result: {}", result);
return result;
}