zoukankan      html  css  js  c++  java
  • 消息幂等与重复消费的问题

    消息幂等与重复消费的问题

    1、什么是消息幂等?

         当出现消费者对某条消息重复消费的情况时,重复消费的结果与消费一次的结果是相同的,并且多次消费并未对业务系统产生任何负面影响,那么这整个过程就可实现消息幂等。

    2、什么情况下需要消息幂等?

        业务开发中,经常会遇到重复提交的情况,无论是由于网络问题无法收到请求结果而重新发起请求,或是前端的操作抖动而造成重复提交情况。 在交易系统,支付系统这种重复提交造成的问题有尤其明显。

        比如:

        用户在APP上连续点击了多次提交订单,后台应该只产生一个订单。在向支付系统发起支付请求的时候,由于网络问题或系统BUG重发,支付系统应该只扣一次钱。 很显然,声明幂等的服务认为,外部调用者会存在多次调用的情况,为了防止外部多次调用对系统数据状态的发生多次改变,将服务设计成幂等。

    3、幂等和防重的区别

        重复提交的情况和服务幂等的初衷是不同的。重复提交是在第一次请求已经成功的情况下,人为的进行多次操作,导致不满足幂等要求的服务多次改变状态。而幂等更多使用的情况是第一次请求不知道结果(比如超时)或者失败的异常情况下,发起多次请求,目的是多次确认第一次请求成功,却不会因多次请求而出现多次的状态变化。

    4、消息重复的场景

    在互联网应用中,尤其在网络不稳定的情况下,消息队列RabbitMQ的消息有可能会出现重复。如果消息重复会影响您的业务处理,请对消息做幂等处理。

    消息重复的场景如下:

    1)发送时消息重复

         当一条消息已被成功发送到服务端并完成持久化,此时出现了网络闪断或者客户端宕机,导致服务端对客户端应答失败。 如果此时生产者意识到消息发送失败并尝试再次发送消息,消费者后续会收到两条内容相同并且Message ID也相同的消息。

    2)投递时消息重复

         消息消费的场景下,消息已投递到消费者并完成业务处理,当客户端给服务端反馈应答的时候网络闪断。为了保证消息至少被消费一次,消息队列RabbitMQ版的服务端将在网络恢复后再次尝试投递之前已被处理过的消息,消费者后续会收到两条内容相同并且Message ID也相同的消息。

    3)负载均衡时消息重复(包括但不限于网络抖动、Broker重启以及消费者应用重启)

         当消息队列RabbitMQ的Broker或客户端重启、扩容或缩容时,会触发Rebalance,此时消费者可能会收到重复消息。

    5、为什么我们要保证幂等性,不保证幂等性,会不会有问题?

         回答这个问题的根源得从业务场景上进行分析。比如正常业务情况下,我们是不允许同个订单重复支付,这种业务场景我们就需要确保幂等性。再比如日志记录,这种业务场景,我们可能就不需要做幂等判断。

         幂等是为了简化客户端逻辑处理,却增加了服务提供者的逻辑和成本,是否有必要,需要根据具体场景具体分析,因此除了业务上的特殊要求外,尽量不提供幂等的接口。

    6、消息队列的消费幂等性如何保证?

         没法保证。前面说了要保证幂等性,得基于业务场景进行考量。

         消息队列本身就不是用来做业务幂等性用的。如果要实现业务幂等性,靠消息队列是没法帮助我们完成的,还得根据自身业务场景,来实现幂等。

    7、常用的业务幂等性保证方法

    1) 利用数据库的唯一约束实现幂等

          比如将订单表中的订单编号设置为唯一索引,创建订单时,根据订单编号就可以保证幂等

    2)利用redis的原子性

         每次操作都直接set到redis里面,然后将redis数据定时同步到数据库中

    3)多版本(乐观锁)控制

          此方案多用于更新的场景下。其实现的大体思路是:给业务数据增加一个版本号属性,每次更新数据前,比较当前数据的版本号是否和消息中的版本一致。如果不一致则拒绝更新数据,更新数据的同时将版本号+1。

    4)状态机机制

         此方案多用于更新且业务场景存在多种状态流转的场景。

    5)token机制

         生产者发送每条数据的时候,增加一个全局唯一的id,这个id通常是业务的唯一标识,比如订单编号。在消费端消费时,则验证该id是否被消费过,如果还没消费过,则进行业务处理。处理结束后,在把该id存入redis,同时设置状态为已消费。如果已经消费过了,则不进行处理。

    总结:

          消息队列没法帮助我们做到消费端的幂等性,消费端的幂等性得基于业务场景进行实现。

          不过消息队列必须得保证消息不能丢至少保证被消费一次,不然消息都丢了,没数据还谈什么业务幂等。在实现消费端处理业务时,要确保消费端是采用手工确认应答机制(ACK),而不是自动应答机制。这样能够确保消费端一旦业务处理失败,生产者还能再次发送同个消息给消费端。

     

    参考链接:

    https://segmentfault.com/a/1190000023555975

  • 相关阅读:
    【第40套模拟题】【noip2011_mayan】解题报告【map】【数论】【dfs】
    【模拟题(63550802...)】解题报告【贪心】【拓扑排序】【找规律】【树相关】
    【模拟题(电子科大MaxKU)】解题报告【树形问题】【矩阵乘法】【快速幂】【数论】
    IMemoryBufferReference and IMemoryBufferByteAccess
    SoftwareBitmap and BitmapEncoder in Windows.Graphics.Imaging Namespace
    Windows UPnP APIs
    编译Android技术总结
    Windows函数转发器
    Two Ways in Delphi to Get IP Address on Android
    Delphi Call getifaddrs and freeifaddrs on Android
  • 原文地址:https://www.cnblogs.com/hld123/p/14688690.html
Copyright © 2011-2022 走看看