架构实战(10)——消息处理中的幂等性

  业务场景

  首先来看一下典型的业务场景,以订单的处理为例:客户下订单,订单系统处理完订单逻辑后,调用支付系统进行支付。用消息队列实现异步调用,使两个系统解耦的同时,也提升了下单的性能,一举三得。有关消息的使用场景,之前已经介绍。

  简化后的处理过程如图:

  

架构实战(10)——消息处理中的幂等性

  业务处理场景

  在这个简单的业务场景中,保证订单被正确支付至关重要,既不能漏掉某个支付请求,也不能为某个订单支付多次。为了做到“不漏掉”,在消息的生产和消费阶段,需要做额外的处理。

  阶段一:消息的生产

  

架构实战(10)——消息处理中的幂等性

  消息的生产过程

  step1:消息被推送到消息中间件。step2:消息中间件把消息持久化到本地硬盘。只有支持消息同步持久化的消息中间件,才能保证消息的可靠性,这是因为消息中间件也可能出故障,故障后没有持久化的消息肯就丢失了。redis支持消费订阅模式,因为内存的易失性,如果redis发生异常,可能导致消息丢失。对消息可靠性要求比较严格的场景,不要用redis的消费订阅模式。step3:生产者得到执行结果。在这一步骤中,如果发生网络抖动或者程序异常,导致生产者无法得到消息推送成功的结果。为了保证消息已经送达,一般采用重试机制,重新向消息中间件推送消息,直到收到成功的结果。这种情况下,导致同一条消息发送多次。

  阶段二:消息的消费

  

架构实战(10)——消息处理中的幂等性

  step1:消费者从消息队列读取消息,然后进行业务处理。step2:处理完成后,调用ack,记录消息已经被处理成功。如果没有调用ack,过段时间消息能够被再次读到。大部分的消息中间件,都能做到消息被至少消费一次,即At-Least-Once策略。如果发生网络抖动,消息中间件没有收到ack,同一条消息将会被消费者处理多次。

  从消息的生产和消费过程分析,为了做到“不漏掉”,经过各种策略保障,却发生了同一条消息可能被处理多次。在这个订单场景中,就代表着订单存在重复支付的现象,发生这种情况,客户估计就要跑光了,公司离倒闭也就不远了。聪明如己的程序员可不能背这个锅,那我们就要想办法,去解决这个问题。

  很明显,解决这个问题,就要做到:同一条订单的支付消息,无论消费到多少次,只能扣一次钱,简单说,这就是幂等性。幂等,是一个数学概念,是指被某个运算操作,运算一次和运算两次结果是一样的。引申到编程中,幂等性是指调用函数处理同一个参数,调用一次或多次,处理结果是一样的。所以,支付系统对于支付消息的处理,应满足幂等性,防止重复支付

  实现幂等性,要怎么做呢?

架构实战(10)——消息处理中的幂等性

  实现幂等性的方法

  step1、设计消息主键。首先要为消息设计业务主键,用以未来判定消息是否重复处理。业务主键根据具体的业务来定,在这个场景中国,支付单号是合适的选择。step2、分布式锁。通过锁机制,避免两个线程同时处理一条消息。如果只有一个实例,可以用线程锁实现;在分布式场景下,必须通过分布式锁实现。分布式锁有多种实现方式,常见的是基于redis、zk的。基于redis的更高效,推荐使用。step3、消息持久化。分布式锁用来避免消息的同时执行,判断消息是否重复处理,只能通过消息的持久化来达到目的。比较简单的做法是引入消息表,在同一个事务中完成业务处理和消息的存储,业务操作前校验消息表中已经存在同样的记录。

  通过以上三步,就可以做到消息处理的幂等性。

  

架构实战(10)——消息处理中的幂等性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值