RocketMQ的消费者消息重试和生产者消息重投

详细介绍了RocketMQ的消息重试机制,RocketMQ的消息重试可以分为生产者重试和消费者重试两个部分。

1 生产者重试

生产者在发送消息时,同步消息失败会重投,异步消息有重试,oneway没有任何保证。消息重投保证消息尽可能发送成功、不丢失,但可能会造成消息重复,消息重复在RocketMQ中是无法避免的问题。消息重复在一般情况下不会发生,当出现消息量大、网络抖动,消息重复就会是大概率事件。另外,生产者主动重发、consumer负载变化也会导致重复消息。

如下生产者属性可以设置消息重试策略:

  1. retryTimesWhenSendFailed:同步发送失败重投次数,默认为2,因此生产者会最多尝试发送retryTimesWhenSendFailed + 1次。不会选择上次失败的broker,尝试向其他broker发送,最大程度保证消息不丢。超过重投次数,抛出异常,由客户端保证消息不丢。当出现RemotingException、MQClientException和部分MQBrokerException时会重投。如果因为超时,那么便不再重试。
  2. retryTimesWhenSendAsyncFailed:异步发送失败重试次数,默认为2,异步重试不会选择其他broker,仅在同一个broker上做重试,不保证消息不丢。如果因为超时,那么便不再重试。
  3. retryAnotherBrokerWhenNotStoreOK:消息刷盘(主或备)超时或slave不可用(返回状态非SEND_OK),是否尝试发送到其他broker,默认false。十分重要消息可以开启。

两个注意点:

  1. 如果同步模式发送失败,则轮转到下一个Broker,如果异步模式发送失败,则只会在当前Broker进行重试。
  2. 发送消息超时时间默认3000毫秒,如果因为超时,那么便不再尝试重试。

2 消费者重试

Consumer消费消息失败后,要提供一种重试机制,令消息至少再消费一次。通常引起消息消费重试的时候包括两种情况:异常重试和超时重试。另外,Consumer在广播模式下重试失效。

2.1 异常重试

由于Consumer端逻辑出现了异常,导致没有返回SUCCESS状态,那么Broker就会在一段时间后尝试重试。

RocketMQ会为每个消费组都设置一个Topic名称为“%RETRY%+consumerGroup”的重试队列(这里需要注意的是,这个Topic的重试队列是针对消费组,而不是针对每个Topic设置的),用于暂时保存因为各种异常而导致Consumer端无法消费的消息,每个Consumer实例在启动的时候就默认订阅了该消费组的重试队列Topic。

考虑到异常恢复起来需要一些时间,会为重试队列设置多个重试级别,每个重试级别都有与之对应的重新投递延时,重试次数越多投递延时就越大(实际上就是配置的延时队列的级别level)。RocketMQ对于重试消息的处理是先保存至Topic名称为“SCHEDULE_TOPIC_XXXX”的延迟队列中,后台定时任务按照对应的时间进行Delay后重新保存至“%RETRY%+consumerGroup”的重试队列中。

2.1.1 并发消费的重试

并发消费可通过客户端参数DefaultMQPushConsumer.maxReconsumeTimes设置最大重试次数,maxReconsumeTimes有效值范围是-1 – 16之间

maxReconsumeTimes默认值为-1,对于并发消费模式,-1就等于16。即并发消费默认最大重试16次,达到最大次数,消息将会发送至死信队列,不再重试。

重试时,延迟等级的控制可以通过MessageListenerConcurrently#consumeMessage方法的ConsumeConcurrentlyContext参数指定重试策略,通过配置ConsumeConcurrentlyContext.delayLevelWhenNextConsume属性的值指定等级策略:

  1. -1,不重试,直接发往死信队列。
  2. 0,默认值,延迟等级broker端控制的,默认从延迟等级level3开始,后续每次重试都是3 + 当前重试次数
  3. 大于0,由client端控制,传入多少延迟等级就是多少。

如果Consumer端逻辑出现异常,实际上重试太多次也没有很大的意义,因此我们可以在代码中指定最大的重试次数,达到一定次数之后就返回SUCCESS,不再重试,对于失败的消息记录到数据库的表中,后续人工处理。

默认的并发消费默认延迟间隔:
在这里插入图片描述

2.1.2 顺序消费的重试

顺序消费同样是通过客户端参数DefaultMQPushConsumer.maxReconsumeTimes设置最大重试次数,超过最大重试次数,消息将被转移到死信队列,范围是-1 – 16之间。

maxReconsumeTimes默认值为-1,对于顺序消费模式来说 -1就代表着Integer.MAX_VALUE,表示无限次本地立即重试消费。这里的重试不再会将消息发往broker重试队列,只在在本地重试。

顺序消费的重试由于不再需要broker控制,那么重试的间隔时间也是通过本地参数控制的,可通过MessageListenerOrderly#consumeMessage方法的ConsumeOrderlyContext参数指定重试策略,通过配置ConsumeOrderlyContext.suspendCurrentQueueTimeMillis属性指定间隔时间,参数取值范围10~30000ms,默认值-1,表示1000ms,即1秒重试一次。

2.2 超时重试

Consumer端的一次消费超时时间为15min,可通过DefaultMQPushConsumer.consumeTimeout属性配置。

2.2.1 并发消费的重试

ConsumeMessageConcurrentlyService#start方法将会通过cleanExpireMsgExecutors定时任务清理过期的消息,启动后15min开始执行,后每15min执行一次,这里的15min是RocketMQ大的默认超时时间,可通过defaultMQPushConsumer#consumeTimeout属性设置。

通过一个定时任务,每隔15min检测一次,当消息消费时间超过15min时,将该消息算作消费失败,并且将该消息通过sendMessageBack发回broker延迟topic,将在给定延迟时间(固定level 3,即延迟10s)之后发回进行重试消费或者发往死信队列。

2.2.1 顺序消费的重试

对于顺序消费,实际上无论超时多久,无论在你的业务逻辑中卡多久,都不会单纯的因为15min的消费超时而重试。这也是为了保证顺序性的妥协,无论执行多久,你的程序终会返回最终结果,只需要根据返回的状态执行对应的逻辑即可。

3 相关源码

如果看了上面的介绍,你还是觉得比较模糊,还是有疑问,你可以选择给我留言或者私信问题,但是更加建议你去读读RocketMQ的源码!

与生产者消息重投相关的源码:

  1. RocketMQ源码(7)—Producer发送消息的总体流程【一万字】
  2. RocketMQ源码(8)—Producer发送单向、同步、异步消息源码【一万字】

与消费者消息重试相关的源码:等待更新……

相关文章:

RocketMQ

如有需要交流,或者文章有误,请直接留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!

评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘Java

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值