zoukankan      html  css  js  c++  java
  • mq要如何处理消息丢失、重复消费?

    如果要你实现一个支付宝向余额宝转账的功能,比如:账户a从支付宝转出5000余额宝转入5000,该怎么做呢?

    可能有些人会说,这还不简单,直接上图

    支付宝先给账户a减5000,调用余额宝的接口给余额宝的账号b加5000。

    用这种方式正常情况下是可以的,如果出现以下问题该怎么办呢?

    1. 调用余额宝api时网络失败了

    2. 调用余额宝api时网络超时了

    3. 如果余额宝api业务逻辑比较复杂,耗时比较长,用户需要长时间的等待才有结果,用户体验不好

    有人说:如果调用余额宝api时网络失败了,对接口进行重试不就可以解决问题了。

    :你是用同步重试,还是异步重试呢?

           如果用同步重试,即在调用余额宝api时获取返回值,如果发现失败立刻重试3次。调用一次余额宝api的耗时为n秒,重试3次的耗时则为3n秒,接口响应时间增加了两倍,增加了接口超时的风险。如果重试3次之后,还是失败该怎么处理?

            如果用异步重试,第一次调用余额宝api时,不管是成功还是失败,都直接给用户返回成功。如果是失败,后台开启一个线程,不断重试一直到成功为止。如果在不断重试的过程中服务器重启了,该怎么办?

    又有人说:如果调用余额宝api时网络超时了,不知道上次请求是成功还是失败,再重试一下不行吗?

    :不是不,第一.余额宝必须做幂等性设计,不然余额宝这边多转入5000怎么办?余额宝肯定不会犯这种错误。第二.同样会面临如果调用余额宝api时网络失败了的问题。

    再有人说:如果余额宝api业务逻辑比较复杂,耗时比较长,用户需要长时间的等待才有结果,用户体验不好。改成异步就可以解决这个问题了。

    答:改成异步可以提前告知用户结果,然后在后台通过补偿机制不断的重试,让数据达成最终一致性,这种方式对用户体验可能确实要好一些。异步处理又分为:开启线程  和 使用mq。线程处理有比较致命的弊端,如果服务器重启,线程里的数据会丢失。

    接下来,我们的重点放在mq上。

    余额宝给账户a减了5000之后,给指定topic1发一条消息,然后余额宝从topic1消费这条消息,给账户b加5000。

    对于问题1,如果余额宝处理失败了,比如像rocketmq这类消息处理框架会把消息放入重试队列重试16次,不需要业务代码做额外的工作。

    对于问题2,如果服务器重启了,由于消息保存在服务端的磁盘上,不会丢失,客户端可以通过offset从服务端重新获取消息,它能够保证消息至少被余额宝消费一次。

    对于问题3,支付宝给账户a减了5000发送完消息之后,可以直接返回成功,然后余额宝作为消费者在后台默默执行,一直到成功为止。

    那么问题又来了:

    如果余额宝消费了消息,业务处理失败了怎么办?这个就是所谓的消息丢失。

    要解决消息丢失就需要建一张消息发送表,如图:

     支付宝从账户a减5000,接着往本地消息表中写入一条消息记录,confirm_status为待确认,然后发送mq消息。注意,支付宝这边的扣款和写本地消息表要在同一事务中。

           余额宝消费消息给账户b加5000之后,调用支付宝消息确认api,修改confirm_status为已确认。

            如果余额宝这边消息丢失了,支付宝有个job会每个5分钟扫描一次本地消息表中confirm_status为待确认状态的记录,重新发送一次消息,这样余额宝又可以重新处理了。

        

    那么还有个问题:

    余额宝这边处理成功,但是由于调用 支付宝消息确认api失败,导致支付宝的job重新发送消息,余额宝重复消费了。这个就是所谓的重复消息。

    重复消费要如何解决呢?

     余额宝也增加一个本地消息表,记录业务处理成功的消息。当然余额宝的账号操作和本地消息表也要在同一个事务中。    

            

            余额宝消费消息之后,先从余额宝的本地消息表中查一下,该消息有没有消费过,如果已经消费过了,则直接调用支付宝消息确认api,修改confirm_status为已确认,避免下次支付宝的job重复发消息。如果从余额宝的本地消息表中查到没有消费,则给账户b增加5000,同时往本地消息表写一条记录,然后调用支付宝消息确认api。

           总结:通过在mq的生产者和消费者两端分别增加本地消息表,并且在生成者端增加定时job扫描待确认状态的记录,重新发送消息,可以解决:消息丢失 和 重复消费 问题。当然实际的支付宝向余额宝转账的场景更复杂,在高并发的情况下,可能需要用分布式锁,防止金额异常。

  • 相关阅读:
    深拷贝浅拷贝
    mock demo
    django 2. 配置信息
    django 1. 入门基础
    java 12. 方法重载
    java 11. 设计规约
    java 10. 参数返回值问题
    java 9. 面向对象之方法设计
    java 8. 面向对象之属性
    java 7. 多维数组
  • 原文地址:https://www.cnblogs.com/SmallStrange/p/13265512.html
Copyright © 2011-2022 走看看