zoukankan      html  css  js  c++  java
  • 聊一聊如何保证RocketMQ使用中如何保证消费幂等性

    之所以想聊一聊这个话题,是因为在刚开始使用rocketmq时,Consumer服务写的有问题的情况下,消息队列会重发,这是因为消费失败会导致消息被放入RETRY重试队列,根据用户配置的重试次数(默认16次)进行重试,这部分我们已经在之前的 RocketMQ存储机制与确认重传机制一文中讨论过,这个情况引起了我探究“什么情况下消息队里会进行重试,会不会导致重复消费?”这一问题的好奇心。

    为什么会出现消息重复的问题?

    对于 Producer

    我们知道,RocketMQ提供了三种发送消息模式

    1、同步发送
    Producer 向 broker 发送消息,阻塞当前线程等待 broker 响应 发送结果。

    DefaultMQProducerImpl中设置如果超时没有响应或者发送失败,会重发。

    2、异步发送
    Producer 首先构建一个向 broker 发送消息的任务,把该任务提交给线程池,等执行完该任务时,回调用户自定义的回调函数,执行处理结果。

    3、Oneway 发送
    Oneway 方式只负责发送请求,不等待应答,Producer 只负责把请求发出去,而不处理响应结果。

    上述前两种发送方式,如果成功发送到消息队列,但消息队列返回结果时出现网络问题,则会导致消息已经发送成功而生产者认为发送失败,重新发送,导致出现多条重复消息。

    对于 Consumer

    1、可能因为 Broker 的消息进度丢失,导致消息重复投递给 Consumer 。
    2、Consumer 消费成功,但是因为网络问题/ JVM 异常崩溃,导致消息消息队列没能收到消费成功确认,以为消费失败,导致重复推送 。

    注:对于大多数消息队列,考虑到性能,消费进度是异步定时同步给 Broker 。

    如何解决

    消费者实现幂等性,一般是在框架层统一封装或者业务层自己实现。

    框架层统一封装

    首先,需要有一个消息排重的唯一标识,该编号只能由 Producer 生成,例如说使用 uuid、或者其它唯一编号的算法 。

    对于RocketMQ来说,Producer 在发送消息时,默认会生成消息编号( msgId ),可见org.apache.rocketmq.common.message.MessageClientExt 类。Broker 在存储消息时,会生成结合 offset 的消息编号( offsetMsgId ) 。Consumer 在消费消息失败后,将该消息发回 Broker 后,会产生新的 offsetMsgId 编号,但是 msgId 不变。

    然后,就需要有一个排重的存储器,例如说:

    使用关系数据库,增加一个排重表,使用消息编号作为唯一主键。
    使用 KV 数据库,KEY 存储消息编号,VALUE 任一。此处,暂时不考虑 KV 数据库持久化的问题。

    那么,我们要什么时候插入这条排重记录呢?

    在消息消费执行业务逻辑之前,插入这条排重记录。但是,此时会有可能 JVM 异常崩溃。那么 JVM 重启后,这条消息就无法被消费了。因为,已经存在这条排重记录。
    在消息消费执行业务逻辑之后,插入这条排重记录。
    如果业务逻辑执行失败,显然,我们不能插入这条排重记录,因为我们后续要消费重试。
    如果业务逻辑执行成功,此时,我们可以插入这条排重记录。但是,万一插入这条排重记录失败呢?那么,需要让插入记录和业务逻辑在同一个事务当中,此时,我们只能使用数据库

    业务层自己实现

    方式很多,这个和 HTTP 请求实现幂等是一样的逻辑:

    先查询数据库,判断数据是否已经被更新过。如果是,则直接返回消费完成,否则执行消费。
    在并发量比较高的系统下,我们可以使用redis来进行幂等性判断。

    正常情况下,出现重复消息的概率其实很小,如果由框架层统一封装来实现的话,肯定会对消息系统的吞吐量和高可用有影响,所以最好还是由业务层自己实现处理消息重复的问题。

    我的做法

    我的项目中主要是针对“任务”提交时进行幂等性判断,我的做法是在下派任务时把任务id放到redis中,然后再任务提交时从redis中删除,但如果消费事务执行失败,redis会重新把任务id添加回来作出补偿。从而完成幂等性判断。

  • 相关阅读:
    N皇后问题(回溯递归思想)
    链表大合集(二) 栈和队列的实现
    我是林荫
    蒟蒻林荫小复习——莫队
    神奇脑洞题解——[HAOI2011]Problem b
    蒟蒻林荫小复习——莫比乌斯反演
    蒟蒻林荫小复习——关于有限制区间元素查询的一些解法
    蒟蒻林荫小复习——K短路的A*解法
    蒟蒻林荫小复习——2-SAT的染色法求最小答案序列
    蒟蒻林荫小复习——克鲁斯卡尔重构树
  • 原文地址:https://www.cnblogs.com/wunsiang/p/12765155.html
Copyright © 2011-2022 走看看