06 | 如何处理消费过程中的重复消息

在消息传递过程中,出现传递失败情况,发送方会重试,重试会导致产生重复消息。

因此使用消息队列的业务系统若没对重复消息处理,就可能会导致系统数据出错。

比如消费订单消息,统计下单金额的服务,就会出现重复统计,导致统计结果出错。

可能有人会问,如果消息队列本身能保证消息不重复,应用程序实现不就简单了。那有消息队列能保证这点么?

消息重复的情况必然存在

MQTT协议中,给出了三种传递消息时能够提供的服务质量标准。质量从低到高,

At most once:最多一次,无消息可靠性,允许丢消息。一般对消息可靠性要求不高的监控场景使用。比如每分钟上报一次机房温度。

At least once:至少一次,不允许丢消息。但允许少量重复出现。

Exactly once:恰好一次,消息不允许丢,也不允许重复,最高等级。

对多有消息队列都是适用的,这个服务质量标准不仅适用于MQTT。现在常用绝大部分消息队列提供的服务质量都是At least once,包括 rabbitmq, rocketmq, kafka。也就是消息队列很难保证消息不重复。

注意,kafka的exactly once是它的事务中支持的一个特性。

既然按照标准大部分消息队列无法保证消息不重复,就需要消费代码能够接受消息是可能重复的这一个现状,然后通过一些方法来消除重复消息对业务的影响。

用幂等性解决重复消费问题

一般解决重复消息的办法是,在消费端的消费消息的操作具备幂等性。

Idempotence: 如果函数f(x) 满足 f(x) = f(f(x)), f(x)满足幂等性。

这个概念拓展到计算机领域,被用来描述一个操作、方法或者服务。

一个幂等的方法,使用同样的参数,对它进行多次调用和一次调用,对系统产生的影响是一样的。

比如设置某个账户余额=100元是幂等的,但是设置某个账户余额+100元不是。执行多次和一次对系统的影响是不一样的。

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

设计幂等操作的方法

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

刚刚提及的不具备幂等特性的转账的例子:将账户X的余额+100,改为限定每个账单每个账户只可以执行一次变更操作。

具体在分布式系统中上述限制方法较多:

方法一:建一张流水表,表有3个字段:转账单ID、账户ID、变更金额。uniq key(转账单ID、账户ID)

不仅关系DB,只要支持INSERT IF NOT EXIST 语义的存储系统都可以实现。比如Redis的SETNX命令。

2. 为更新的数据设置前置条件

以前述账户余额+100为例,改为当账户余额为500的时候+100,就保证了幂等性。

有时候更新字段并不是数字,则可以加一个版本号字段,同时对版本号进行判断和递增。

3. 记录并检查操作

通用性最强,适用范围最广的实现幂等性的方法。

在执行数据更新操作前,先检查一下是否执行过这个更新操作。

具体的实现方法是,在发送消息时,每条消息执行全局唯一ID,消费时,先根据这个ID检查这条消息,没被消费过,再执行数据更新,再讲消息状态置为已消费。

分布式系统中,要做到这点不简单。

1. 全局唯一ID的生成

2. 检查消费状态,更新数据,更新消费状态,三个操作的原子性;

第一个点,在分布式系统中,很难找到一个简单、高可用、高性能的实现方案。

第二个点, 在分布式系统中,可以用分布式事务或者分布式锁,但都不是容易的事。

小结

主要介绍通过幂等来解决消息重复消费的问题。

实现幂等的方案主要有,利用数据库的唯一性约束,为数据更新设置一次性的前置条件,记录并检查操作。

这些实现幂等的方法,不仅可以用于重复消息的问题,也适用于,

在其他场景中来解决重复请求或者重复调用的问题。

比如HTTP服务;

APP或前端重复提交表单;

也可以将微服务设计成幂等,解决RPC框架自动重试导致的重复调用问题;

思考

为什么大部分消息队列都只是 at least once,而不是excatly once的实现呢?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值