谈谈Rocketmq消息重试机制

1,223 阅读2分钟

这是我参与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

  1. 解压
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次之后停止。 image.png

我们带着这个问题, 去看一下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;
}