• RabbitMQ系列(四)--消息如何保证可靠性传输以及幂等性


    一、消息如何保证可靠性传输

    1.1、可能出现消息丢失的情况

      1、Producer在把Message发送Broker的过程中,因为网络问题等发生丢失,或者Message到了Broker,但是出了问题,没有保存下来

      针对这个问题,Producer可以开启MQ的事务,如果这个过程出现异常,进行回滚,但是有个很大的问题,你提交一个事务就会阻塞在那,

    非常影响性能,生产环境肯定不会开启事务,一般都是使用confirm机制

      2、Broker接收到Message暂存到内存,Consumer还没来得及消费,Broker挂掉了

      可以通过持久化设置去解决:

        1).创建Queue的时候设置持久化,保证Broker持久化Queue的元数据,但是不会持久化Queue里面的消息

        2).将Message的deliveryMode设置为2,可以将消息持久化到磁盘,这样只有Message支持化到磁盘之后才会发送通知Producer ack

      这两步过后,即使Broker挂了,Producer肯定收不到ack的,就可以进行重发

      3、Consumer有消费到Message,但是内部出现问题,Message还没处理,Broker以为Consumer处理完了,只会把后续的消息发送

      这时候,就要关闭autoack,消息处理过后,进行手动ack

    1.2、一般通过生产端保证可靠性投递

      1、保证消息的成功发出

      2、保证MQ节点的成功接收

      3、发送端收到MQ节点(Broker)的确认应答

      4、完善的消息补偿机制

    1.3、解决方案

    1、消息落库,对消息状态进行变更,对于高并发环境下数据库压力很大,因为需要写多次数据库

    整体流程:

      1、业务数据和消息都进行落库

      2、生产端发送message给Broker

      3、Broker给Confirm响应返回生产端

      4、接收到confirm,对message状态更改

      5、分布式定时任务获取消息的状态

      6、如果消息不能成功投递,重新进行发送,记录重发次数

      7、当重发3次之后,将状态修改,只能人工进行干预

    2、消息的延迟投递,做二次确认,回调检查。适合高并发环境,减少写库的次数

     整体流程:

      1、上游服务首先将业务代码入库,发送message给Broker

      2、发送第二个延迟确认消息

      3、下游服务监听消息进行消费

      4、发送确认消息,这里不是confirm机制,而是一条新的消息

      5、通过回调服务监听这个confirm消息,然后把消息进行入库

      6、回调服务检查到延迟确认消息,会在数据库查询是否有这条消息

      7、如果没有查到这条消息,回调服务通过RPC给一个重新发送命令到上游系统

    相比第一种方案,这里减少了一次message入库,confirm机制是消息可靠性投递的一个核心,在下篇文章会讲到

    二、如何保证消息的幂等性

      首先,无论是RabbitMQ、RocketMQ还是kafka,都有可能出现消息的重复发送,这个是MQ无法保障的,而幂等性是开发或者运维人员需要保证的

      所谓消息的幂等性是指即使收到多次消息,也不会重复消费,这点很重要,例如用户付钱,点的太快了,付了多次,但是你也只能扣一次钱,

    不然要骂人了

    2.1、RabbitMQ可能导致出现非幂等性的情况

      1、可靠性消息投递机制:consumer回复confirm出现网络闪断,producer没有收到ack,定时任务轮询可能就会重新发送消息,这样consumer就

    会收到两条消息

      2、MQ Broker与消费端传输消息的过程出现网络抖动

      3、消费端故障或异常

    2.2、kafka可能出现非幂等性的情况

    在Consumer端offset没有提交的时候,Consumer重启了,这时候就会出现重复消费的情况

    2.3、解决方案

    1、唯一ID+指纹码

      整体实现相对简单,需要进行数据库写入,利用数据库主键去重,使用ID进行分库分表算法路由,从单库的幂等性到多库的幂等性

      1).这里唯一ID一般就是业务表的主键,比如商品ID

      2).指纹码:每次操作都要生成指纹码,可以用时间戳+业务编号+...组成,目的是保证每次操作都是正常的

    整体流程:

      1、需要一个统一ID生成服务,为了保证可靠性,上游服务也要有个本地ID生成服务,然后发送消息给Broker

      2、需要ID规则路由组件去监听消息,先入库,如果入库成功,证明没有重复,然后发给下游,如果发现库里面有了这条消息,就不发给下游

    2、利用Redis的原子性实现

      Redis的实现性能比较好,而且Redis一般使用集群,不用担心某台机器挂掉了,影响服务。

    存在的问题:

      是否要进行数据落库,如果落库的话,怎么保证缓存和storage的一致性、事务,如果不落库,如何设置定时同步策略

  • 相关阅读:
    转:va_list、va_start、va_arg、va_end的原理与使用
    学习Docker的记录
    Google Code 优秀的开源工具
    转载(程序在内存中运行的奥秘)
    C# 和 Java 之争之我见
    揭秘ASP.NET 2.0的Eval方法(转)
    IIS6.0 架构(二)
    IE6 position:fixed bug (固定窗口方法)(转载)
    用FileStream上传图片转换成二进制,在本地用行,传到服务器上去出现如下错误
    异常处理
  • 原文地址:https://www.cnblogs.com/huigelaile/p/10917859.html
Copyright © 2020-2023  润新知