36 消息处理失败场景下死信队列的解决方案

1. 消息处理失败的场景-优惠券系统的数据库宕机

在基本确保MQ的消息不丢失,且同时不会对消息进行重复处理的情况下,在正常流程下,基本就没什么问题了。

在MQ使用没问题之后,这里要考虑一个问题,那就是如果消费者所在的优惠券系统的数据库宕机了该怎么办?

在前面的场景中,订单支付成功之后会推送消息到MQ,然后优惠券系统、红包系统会从MQ里获取消息去执行后续的处理,比如发红包或者发优惠券。

如果优惠券系统的数据库宕机了,就会导致消费者端所在的系统从MQ里获取到消息之后没办法进行处理。

 在上述的场景中,需要考虑以下几个问题:

  1. 优惠券系统应该怎么对消息进行重试?
  2. 重试多少次才行?
  3. 如果反复重试依旧无法成功,这时消息应该怎么处置?保留还是丢掉?

2.数据库宕机的时候,避免直接返回 CONSUME_SUCCESS

在MQ的消费者逻辑中,我们注册了一个监听器回调函数,当Consumer获取到消息之后,就会叫个该函数去处理。

在之前的分析中,当消费者的逻辑在回调函数中对消息进行处理,比如发红包、发优惠券之类的,处理完成之后,就可以返回一个状态告诉RocketMQ Consumer这批消息的处理结果。

按照正常的流程,如果返回的是CONSUME_SUCCESS,那么Consumer就知道这批消息处理完成了,就会对提交这批消息的Offset到broker去,然后下次就会继续从broker获取下一批消息来处理了。

但是如果在上面的回调函数中,对一批消息发优惠券的时候,因为数据库宕机了,导致优惠券发放逻辑无法完成,此时就不能返回CONSUME_SUCCESS状态了。

如果你返回的话,下一次就会处理下一批消息,但是这批消息其实没处理成功,此时必然导致这批消息就丢失了。

肯定会导致有一批用户没法收到优惠券的。

3. 如果对消息的处理有异常,可以返回RECONSUME_LATER状态

在面对上述数据库宕机的问题,会导致这批消息的处理是异常的,此时没法处理这批消息,我们就应该返回一个 CONSUME_LATER 状态。

CONSUME_LATER 的意思是,我现在没法完成这批消息的处理,麻烦你稍后过段时间再次给我这批消息让我重新试一下!

 上述的代码与之前的做出了修改,如果消息处理失败了,就会进入catch() 语句块中返回 RECONSUME_LATER 状态,让RocketMQ稍后再重新把这批消息给我,让我重试对这批消息进行处理。

4. RocketMQ发起消费重试的原理

 RocketMQ 在收到消费者返回的 RECONSUME_LATER 状态之后,让消费者进行消费重试的逻辑如下:

简单来说,RocketMQ 会有一个针对该 ConsumerGroup 的重试队列。如果消费端返回了 RECONSUME_LATER 状态,它会把这批消息放到该消费组的重试队列中去。

这里要说明的是,MQ将消息放到消费组的重试队列是基于返回的 RECONSUME_LATER状态后自己去处理,不需要再新增其他配置或代码去触发了。

场景说明:

假如你的消费组的名称是“VoucherConsumerGroup” ,也就是说这优惠券系统的消费组,它对应的就会有一个“%RETRY%VoucherConsumerGroup” 这个名字的重试队列。

 在经过一段时间之后,重试队列中的消息会再次推送给我们的消费者,让我们进行处理。如果再次失败,就又返回了 RECONSUME_LATER,如此返回多次,默认最多是重试16次!

在多次重试消息的过程中,每次重试之间的间隔时间是不一样的,这个间隔时间可以如下进行配置:

 messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h

上面这段配置的意思是,第一次重试是1秒后,第二次重试是5秒后,以此类推,知道重试第16次是2h后。

5.如果连续重试16次还是无法处理消息,进入“死信队列”

连续重试16次在最大程度上保证了消息处理成功。但也有例外,当在16次重试范围内都无法成功处理,就需要另一个队列了,也就是死信队列。

死信队列,意思是死掉的队列就放这个队列里。也就是一批消息在重试16次之后还无法成功处理掉,就不再重试消息,而是直接被判定为死掉了,会自动进入到死信队列里去。

死信队列的名字是 “%DLQ%VoucherConsumerGroup” ,这是可以在 RocketMQ 的管理后台上看到的。

 对于死信队列中的消息的处理方式如下:

这是看使用场景而定,比如我们可以专门开一个后台线程,去订阅 “%DLQ%VoucherConsumerGroup” 这个死信队列,对死信队列中的消息,继续不停的重试处理。

6. 消息处理失败场景下的方案总结

在MQ消费者底层的发生故障,比如发生数据库宕机、缓存宕机等情况时,消费者系统就无法完成消息的处理,此时可以通过一些返回状态让消息进入 RocketMQ 自带的重试队列;如果重试16次消息处理后还是失败,就可以让消息进入RocketMQ自带的死信队列,后续针对死信队列中的消息进行单独的处理就可以了。

7. 扩展

(1)消费失败返回 RECONSUME_LATER状态,消息进入重试队列,此时消息处理失败,是不会提交offset 的。

(2)死信队列和正常消息一样是一个Topic,所以定期会在磁盘里面被清理掉。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值