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

  • 相关阅读:
    Python进阶03 模块
    Python进阶02 文本文件的输入输出
    Python进阶01 词典
    Python基础10 反过头来看看
    Python基础09 面向对象的进一步拓展
    Python基础08 面向对象的基本概念
    Python基础07 函数
    Vuex源码分析(转)
    Vue2.x双向数据绑定
    Angular2的双向数据绑定
  • 原文地址:https://www.cnblogs.com/hld123/p/14688690.html
Copyright © 2011-2022 走看看