RocketMQ实战—5.消息重复+乱序+延迟的处理

大纲

1.根据RocketMQ原理分析为什么会重复发优惠券

2.引入幂等性机制来保证数据不会重复

3.如何用死信队列处理优惠券系统数据库宕机

4.基于RocketMQ的订单库同步为什么会消息乱序

5.如何解决RocketMQ的消息乱序问题

6.RocketMQ的顺序消息机制的代码实现

7.基于RocketMQ的数据过滤机制提升处理效率

8.基于延迟消息机制优化订单的定时退款扫描问题

9.RocketMQ的延迟消息的代码实现

10.RocketMQ的生产实践经验总结

1.根据RocketMQ原理分析为什么会重复发优惠券

(1)客服反馈有用户重复收到了多个优惠券

(2)问题定位为优惠券系统重复消费了消息

(3)订单系统因处理超时被重复回调

(4)订单系统因发送RocketMQ异常重试发送

(5)优惠券系统重复消费一条消息

(6)消息重复问题是一种常见的问题

(1)客服反馈有用户重复收到了多个优惠券

有用户在支付一个订单后,一下子收到了多个优惠券,本来按照规则只应该有一个优惠券的。也就是说,订单系统给用户重复发放了多个优惠券。

(2)问题定位为优惠券系统重复消费了消息

现在订单系统已和各个系统进行了解耦,当订单支付成功后,会发送一条消息到MQ。然后红包系统会从MQ里获取消息进行红包派发,优惠券系统会从MQ里获取消息进行优惠券派发,其他系统也是同理。

但是现在出现了一个奇怪的问题:就是优惠券系统似乎对同一条消息重复处理了两次,导致给一个用户重复派发了两个优惠券。

图片

优惠券重复派发两次的问题已经定位到了,就是:优惠券系统对同一条订单支付成功的消息处理了两次,导致给用户重复发放了优惠券。那么接下来的问题是,为什么优惠券会对同一条消息重复处理两次。

要明白为什么优惠券系统对同一条消息重复处理了两次,先来研究第一个问题:订单系统收到一个支付成功的通知后,它在发送消息到MQ时,是否会重复把一条消息发送两次。

(3)订单系统因处理超时被重复回调

首先考虑第一种情况:假设用户在支付成功后,订单系统收到了一个支付成功的通知,接着它就向MQ发送了一条订单支付成功的消息。但是偏偏可能因为某些原因,导致订单系统处理的速度有点慢。

图片

然后可能因为订单系统处理的速度有点慢,导致支付系统跟订单系统之间的请求出现了超时。此时支付系统便有可能再次重试调用订单系统的接口,去通知这个订单已经支付成功了。然后订单系统这时可能又一次推送了一条消息到MQ,相当于一条订单支付成功的消息,重复推送了两次到MQ,于是MQ里就有两条同样的订单支付成功消息。

图片

之后,优惠券系统便会消费到这两条重复的消息,重复派发两个优惠券给用户。由此可见,发送消息到MQ的订单系统,如果出现了接口超时等问题,可能会导致上游的支付系统重试调用订单系统的接口,从而导致订单系统对同一条订单支付成功的消息重复发送两次到MQ。

图片

(4)订单系统因发送RocketMQ异常重试发送

接着考虑第二种情况:假设订单系统为了保证消息一定能投递到MQ,而采用了重试的代码,如下所示。这种重试的方式是一把双刃剑,正是因为这个重试可能导致消息重复发送。

try {
  
      //执行订单本地事务    orderService.finishOrderPay();    //发送消息到MQ去    producer.sendMessage();} catch (Exception e) {
  
      //如果发送消息失败了,进行重试    for (int i=0; i<3; i++) {
  
          //重试发送消息    }    //如果多次重试发送消息后,还是不行,就回滚本地订单事务    orderService.rollbackOrderPay();}

假设生产者发送了一条消息到MQ,其实MQ已经接收到这条消息了。但是MQ返回响应给生产者时,网络有问题超时了,导致生产者没能及时收到MQ返回来的响应。此时MQ里已经有生产者发送过来的消息了,只不过它返回给生产者的响应没能及时给到生产者而已。

图片

这时订单系统的代码里可能会有一个网络超时的异常,然后订单系统就会进行重试,再次发送这条消息到MQ里去。于是MQ也就会收到一条一模一样的消息,从而导致消息重复发送了。所以这种重试代码在使用时一定要小心,因为它还是有一定的概率会导致生产者重发消息的。

图片

(5)优惠券系统重复消费一条消息

即使生产者没有重复发送消息到MQ,哪怕MQ里就一条消息,优惠券系统也有可能会重复消费,这是为什么呢?

假设优惠券系统拿到了一条订单成功支付的消息,然后都已经进行了处理,都已经对这个订单的用户发放了一张优惠券。根据前面介绍,这时优惠券系统应该返回一个CONSUME_SUCCESS的状态,然后提交消费进度offset到Broker。

但是碰巧的是,优惠券系统刚刚发放完优惠券,还没来得及提交消息offset到Broker,优惠券系统就进行了一次重启。比如可能优惠券系统的代码更新了,需要重启进行重新部署。

图片

这时候因为优惠券系统没提交这条消息的offset给Broker,Broker并不知道优惠券系统已经处理完了这条消息。然后优惠券系统重启后,Broker就会再次把这条消息交给它进行处理。于是优惠券系统便会再次发送一张优惠券,从而导致重复发送了两次优惠券。这就是对同一条消息,优惠券系统重复处理两次的原因。

图片

(6)消息重复问题是一种常见的问题

实际上类似优惠券系统这样的业务系统,肯定是会频繁更新代码的,可能每隔几天就需要重启一次系统进行代码更新。所以,重启优惠券系统时,可能有一批消息刚处理完还没来得及提交offset给Broker,然后重启后会再一次重复处理这批消息,这种情况发生的概率比较大。

另外对于系统之间的调用,出现超时和重试的情况也是很常见的。所以负责发消息到MQ的系统,很可能时不时出现一次超时,然后被其他系统重试调用其接口,于是生产者可能就会重复发送一条消息到MQ。

所以,消息重复问题是一种常见的问题。

2.引入幂等性机制来保证数据不会重复

(1)什么是幂等性机制

(2)发送消息到RocketMQ时如何保证幂等性

(3)基于查询RocketMQ的业务判断法

(4)基于Redis缓存的状态判断法

(5)没必要保证生产者不重复发送消息

(6)如何保证消费者处理消息的幂等性

(7)RocketMQ消息幂等性的方案总结

(1)什么是幂等性机制

幂等性机制,就是避免对同一请求或同一消息进行重复处理的机制。幂等性指的是:比如有一个接口,如果调用方对该接口的一次请求重试了多次,那么该接口需要保证自己系统的数据是正常的,不能多出一些重复的数据。

幂等对于RocketMQ而言,就是从RocketMQ里获取消息时,要保证对同一条消息只能处理一次,不能重复处理多次,导致出现重复的数据。因此要解决消息重复问题,关键就是要引入幂等性机制。

(2)发送消息到RocketMQ时如何保证幂等性

订单系统发送消息到RocketMQ,需要保证幂等性吗?由于订单系统的接口可能会被重复调用导致发送重复的消息到RocketMQ,也可能有自己的重试机制导致发送重复的消息到RocketMQ。如下图示:

图片

那么如果想要让订单系统别发送重复的消息到RocketMQ去,应该怎么做呢?大体上来说,常见的方案有两种。第一个方案是业务判断法,第二个方案是状态判断法。

(3)基于查询RocketMQ的业务判断法

也就是说,订单系统必须要知道是否已成功发送消息到RocketMQ,消息是否已在RocketMQ里。

举个例子,当支付系统重试调用订单系统的接口时,订单系统可以发送一个请求到RocketMQ,查询一下当前RocketMQ里是否存在针对这个订单的支付消息。如果RocketMQ响应订单系统之前已经写入过这条消息了,那么订单系统就可以不用发送这条消息到RocketMQ了。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值