摘要:比如一个消费订单消息,统计下单金额的微服务。若不正确处理重复消息,就会出现重复统计。那仅靠MQ能保证消息不重复吗?

本文分享自华为云社区《如何处理消费过程中的重复消息?》,作者:JavaEdge。

消息传递过程中若失败,则发送方会执行重试,重试就可能产生重复消息。若不处理重复消息,可能收获惊喜。比如一个消费订单消息,统计下单金额的微服务。若不正确处理重复消息,就会出现重复统计。那仅靠MQ能保证消息不重复吗?

消息重复必然存在,在MQTT协议,给出三种传递消息时能够提供的

1 服务质量标准

服务质量从低到高:

At most once

至多一次。消息在传递时,最多被送达一次。即没什么消息可靠性保证,允许丢消息。一般都是一些对消息可靠性要求不太高的监控场景使用,比如每分钟上报一次机房温度数据,可接受数据少量丢失

At least once

至少一次。消息在传递时,至少会被送达一次。即不允许丢消息,但允许少量重复消息

Exactly once

恰好一次。消息在传递时,只会被送达一次,不允许丢失也不允许重复

服务质量标准不仅适于MQTT,对所有MQ都适用。大部分MQ提供服务质量都是At least once,如RocketMQ、RabbitMQ和Kafka。可以说MQ本身并不保证消息不重复。

你不对,我看过Kafka文档,Kafka支持Exactly once的!

没错,Kafka的确支持Exactly once,但本文说的也没问题。Kafka的“Exactly once”和消息传递服务质量标准中的“Exactly once”不同,它是Kafka提供的另一特性,Kafka中支持的事务也和通常理解的事务有差异。Kafka中的事务和Excactly once主要为配合流计算。

既然MQ无法保证消息不重复,就得消费代码接受“消息可能重复”这个现实,通过业务代码解决重复消息对业务的影响。

2 幂等性

一般解决重复消息方案就是在消费端,让消费消息的操作具备幂等性(Idempotence):
描述一个操作、方法或者服务,其任意多次执行所产生的影响均与一次执行的影响相同。

一个幂等的方法,使用同样参数,对它进行多次调用和一次调用,对系统产生影响一样。所以,对幂等方法,无需担心重复执行会改变系统。

示例

不考虑并发,“将账户X的余额设为100元”,执行一次后对系统的影响是,账户X的余额变成了100元。只要提供参数100元不变,执行多少次,账户X余额始终100,这操作就是个幂等操作。

“将账户X余额加100元”,这操作就不是幂等,每执行次,账户余额增加100,执行多次和执行一次对系统的影响(即账户余额)不同。

若系统消费消息的业务逻辑具幂等性,那就不用担心消息重复,因为同一消息,消费一次和多次对系统影响一样。即消费多次等于消费一次。

从对系统影响结果:At least once + 幂等消费 = Exactly once。

3 幂等实现方案

最好从业务逻辑入手,将消费业务设计成具备幂等性的操作。但也不是所有业务都天然幂等,需要一些技巧。

3.1 数据库唯一约束

比如对于:将账户X余额加100。

可限制对每个转账单,每个账户只能执行一次变更操作。最简单的,在DB中建一张【转账流水表】:

  • 转账单ID
  • 账户ID
  • 变更金额

然后给【转账单ID,账户ID】联合起来创建唯一约束,这样相同转账单ID、账户ID,表里至多只存在一条记录。

消费消息逻辑可变为:“在【转账流水表】增加一条转账记录,再根据转账记录,异步更新用户余额。”
在转账流水表加条转账记录操作中,由于【转账单ID,账户ID】唯一约束,对同一转账单,同一账户只能插一条记录,后续重复插入操作都会失败,这就实现了幂等。

所以,只要是支持类似“INSERT IF NOT EXIST”语义的存储系统都可实现幂等。
比如,可用

Redis的SETNX

替代数据库中的唯一约束,实现幂等消费。

3.2 为更新的数据设前置条件(类似CAS)

给数据变更设置一个前置条件:

  • 满足条件就更新数据
  • 否则拒绝更新数据

更新数据时,同时变更前置条件中需要判断的数据。于是,重复执行该操作时,由于第一次更新数据时,已变更前置条件中的判断数据,不满足前置条件,则不会再执行更新。

“将账户X的余额增加100元”,这操作加个前置条件,变为:“若账户X当前余额为500元,将余额加100元”就具备幂等性。对应到MQ消息,在消息体中带上当前余额,消费时判断DB中当前余额==消息中的余额,相等时才执行更新。

但要更新数据不是数值,或要做个复杂的更新操作咋办?前置判断条件是啥呢?

MVCC

更通用的,是给数据增加版本号version属性,每次更新数据前,比较

当前数据version == 消息中的version

  • 不一致,拒绝更新
  • 一致,更新数据同时将版本号+1,一样则可实现幂等更新

3.3 记录并检查操作

若前两种方案都不适用,还有通用性最强、适用范围最广方案:记录并检查操作,也称“Token机制或GUID(全局唯一ID)机制”,执行数据更新操作前,先检查是否执行过这更新操作。

  • 发消息时,给每条消息指定全局唯一ID
  • 消费时,先根据ID检查消息是否被消费过,若没有,才更新数据并将消费状态置为已消费

但分布式系统下很难实现:

  • 首先,给每个消息指定一个全局唯一ID,方法很多,但都不太好同时满足简单、高可用和高性能,或多或少都有牺牲
  • 更麻烦的,“检查消费状态,然后更新数据并设置消费状态”,三个操作必须作为一组操作,保证原子性,才能真正实现幂等,否则就是Bug

比如对于同一消息:“全局ID为8,操作为:给ID为666账户增加100元”,可能出现这样情况:

  • t0时刻:Consumer A 收到条消息,检查消息执行状态,发现消息未处理过,开始执行“账户增加100元”
  • t1时刻:Consumer B 收到条消息,检查消息执行状态,发现消息未处理过,因这时刻,Consumer A还未来得及更新消息执行状态
  • 这样就导致账户被错误地增加了两次100元,这是一个在分布式系统中非常容易犯的错误

对此,可以用事务实现,也可以锁,但在分布式系统下,分布式事务、分布式锁都会引入高复杂度。所以一般不推荐。

总结

这些幂等方案不仅可用于解决重复消息问题,也可解决重复请求或重复调用问题。比如:

  • 将HTTP服务设计成幂等的,解决前端或APP重复提交表单数据的问题
  • 将一个微服务设计成幂等的,解决RPC框架自动重试导致的重复调用问题

为何MQ都只提供At least once服务质量,而非Exactly once

若MQ实现exactly once,会引发:

  • 消费端pull时,需检测此消息是否被消费,这检测机制无疑拉低消息消费速度。随消息剧增,消费性能势必急剧下降,导致消息积压
  • 检查机制还需业务端去配合实现,若一条消息长时间未返回ack,MQ需要去回调看下消费结果(类似事务消息的回查机制)。这就增加业务端的压力与未知因素。
  • 为了确保消息没有被丢失或者重复,队列需采取一定的类似回查的手段,检测消费者是否有收到消息进行处理,在一定程度上会导致队列堆积等一系列问题,并且队列实现的复杂度上升
  • 从消费者的角度而言,因为消费者端和Broker Service端都是会各自集群,消费者端可能会存在网络抖动,导致Broker Service为了确保消息不丢失和重复,需要一直进行回查类似的操作,但是由于网络问题,导致队列堆积。

所以,MQ不实现exactly once,而是at least once + 幂等性,而幂等性我们消费端业务代码自己处理。

MQ即使做到Exactly once级别,Con也要做幂等。因为Con从MQ取消息时,若Con消费成功,但ack失败,Con还是会取到重复消息,所以MQ费力做成Exactly once无法避免业务侧消息重复问题。

使用DB的唯一索引防止消息被重复消费,若业务系统存在分库分表,消费消息被路由到不同库或表,还是会存在问题?

一般也不会有问题,因为使用我们的方法,一条具体消息,总会落到确定的库表,其重复消息也会落地同样库表。

若队列实现At least once,但为不丢消息,Broker Service会进行一定重试,但不可能一直重试,若就是一直重试还是失败怎么处理?

有的MQ会有个特殊队列,保存这些总是消费失败的“坏消息”,然后继续消费之后的消息,避免这些坏消息卡死队列。这种坏消息一般不会是因为网络原因或消费者宕机导致的,大多都是因为消息数据本身有问题,消费者的业务逻辑无法处理。

exactly once,实现有性能损耗,并发高时易出现消息堆积;消息队列设计初衷是解决解耦,而解耦的对象往往是高并发,对性能要求较高的,从产品需求层面讲,消息队列设计更注重性能,而非精准(exactly once);基础架构角度来说,关注点是占比大的需求(不能不发,可以重发),占比极小的需求(敏感型,只能触发一次)可以单独抽出来另外实现。最后,请教老师有没有比较具体的业务场景,非用这种exactly once不可的。

点击关注,第一时间了解华为云新鲜技术~

电商场景下,如何处理消费过程中的重复消息?相关推荐

  1. 【问答集锦】人工智能/机器学习技术在电商场景下的应用

    近年来阿里不断运用深度学习.强化学习等人工智能领域的相关知识优化自身电商平台的搜索引擎和推荐系统,让其从冷冰冰的系统不断成长为越来越懂用户的智能购物助手. 日前,<尽在双11>人工智能部分 ...

  2. 阿里巴巴机器翻译在跨境电商场景下的应用和实践

    摘要: 本文将与大家分享机器翻译相关背景知识,再深入介绍机器翻译在阿里生态中的具体应用实践,介绍基于机器翻译技术搭建的一套完善的电商多语言解决方案,最后将会从技术角度介绍阿里机器翻译在解决实际业务问题 ...

  3. GATNE:阿里电商场景下的大规模异构网络表示学习 KDD2019

    论文链接:https://arxiv.org/pdf/1905.01669.pdf 代码链接:https://github.com/THUDM/GATNE 论文来源:KDD2019 摘要 网络嵌入(图 ...

  4. LSM优化系列(五) -- 【SIGMOD‘19】X-engine 在电商场景下针对大规模事务处理的优化-- 强者恒强啊

    文章目录 1. 前言 2. 论文结构 2.1 海啸 问题 2.2 泄洪 问题 2.3 洋流 问题 3. X-engine架构 3.1 读路径优化 概览 3.2 写路径优化概览 3.3 Flush和Co ...

  5. 强化学习在电商环境下的若干应用与研究

    背景 随着搜索技术的持续发展,我们已经逐渐意识到监督学习算法在搜索场景的局限性: 搜索场景中,只有被当前投放策略排到前面的商品,才会获得曝光机会,从而形成监督学习的正负样本,而曝光出来的商品,只占总的 ...

  6. ssm电商背景下精品茶网站的设计与实现毕业设计-附源码191732

    摘 要 近年来,随着移动互联网的快速发展,电商背景下精品茶网站越来越受到网民们的欢迎,电商背景下精品茶网站对国家经济的发展也起着越来越重要的作用.简单的流程.便捷可靠的支付方式.快捷畅通的物流快递.安 ...

  7. 互联网时代下,看租赁电商如何玩转消费金融

    文章讲的是互联网时代下,看租赁电商如何玩转消费金融,随着共享单车受到市场的火爆追捧,共享经济概念开始深入人心.目前来说,共享经济本质都是以租赁的形式呈现.小规模的时候,看着更像一个服务公司,但是规模一 ...

  8. Java生鲜电商平台-SpringCloud微服务架构中分布式事务解决方案

    Java生鲜电商平台-SpringCloud微服务架构中分布式事务解决方案 说明:Java生鲜电商平台中由于采用了微服务架构进行业务的处理,买家,卖家,配送,销售,供应商等进行服务化,但是不可避免存在 ...

  9. 我在京东怎么做运营——电商思维下的产品运营及管理

    赵飞谭(微信号zhao_feitan),京东高级产品运营,原紫光华宇市场经理,也曾在神州数码就职.多年营销/运营经验.产品经验,2015负责京东3C事业部dostyle品牌3大业务线11个三级品类业务 ...

最新文章

  1. python 静态方法与类方法
  2. 【PAT乙级】1040 有几个PAT (25 分)
  3. 狼的十大“处世哲学”~~(有关狼的图片)
  4. oracle exp imp
  5. C#中读写INI文件
  6. 高等数学下-赵立军-北京大学出版社-题解-练习8.1
  7. Python 线程条件变量 Condition - Python零基础入门教程
  8. 02241107班编程题 切片逆序大小写等
  9. 通过wifi上网,桥接模式下virtualBox虚拟机无法连上网的解决办法
  10. Python+OpenCV:ORB: An efficient alternative to SIFT or SURF
  11. excel随机抽取_Python自制班级点名器让Excel表格用起来
  12. 图片渲染延迟_Qt入门DirectX11学习之旅(六)DirectX11 GBuffer Deffered延迟渲染
  13. Spring教程动画文字版
  14. 运行bat时隐藏cmd窗口的方法(bat隐藏窗口 隐藏运行bat文件)
  15. 番外4:自动进行功放输出阻抗匹配设计(匹配至4次谐波)
  16. 通过财务报表读懂美股
  17. Python L型组件填图问题(棋盘覆盖问题)
  18. Codevs 3322 时空跳跃者的困境(组合数 二项式定理)
  19. 论游戏中Buff的实现
  20. 六级阅读真题词组(2016)

热门文章

  1. 表面上承认错误_做一个可以承认错误的领导者
  2. 视觉SLAM笔记(5) 编程基础
  3. 深度学习笔记(49) 风格代价函数
  4. c语言printf族函数,C语言中的printf族函数深入详解
  5. shiro 同时实现url和按钮的拦截_shiro笔记 【四】
  6. mvc移动创建oracle表,使用 ASP.NET MVC (C#)在15分钟内创建电影数据库应用程序 | Microsoft Docs...
  7. c语言程序设计指针教学,C语言程序设计中指针教学要点分析
  8. 先滑窗后时空联合处理MATLAB,时空联合优化重建方法及系统与流程
  9. 二分- Count on Canton
  10. 关于Linux环境变量