支付业务系统---幂等性

什么是幂等性

  • HTTP/1.1中对幂等性的定义是:一次和多次请求某一个资源对于资源本身应该具有同样的结果(网络超时等问题除外)。也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同。

这里需要关注几个重点:

  1. 幂等不仅仅只是一次(或多次)请求对资源没有副作用(比如查询数据库操作,没有增删改,因此没有对数据库有任何影响)。
  2. 幂等还包括第一次请求的时候对资源产生了副作用,但是以后的多次请求都不会再对资源产生副作用。
  3. 幂等关注的是以后的多次请求是否对资源产生的副作用,而不关注结果。
  4. 网络超时等问题,不是幂等的讨论范围。

幂等性是系统服务对外一种承诺(而不是实现),承诺只要调用接口成功,外部多次调用对系统的影响是一致的。声明为幂等的服务会认为外部调用失败是常态,并且失败之后必然会有重试。

什么情况下需要幂等

业务开发中,经常会遇到重复提交的情况,无论是由于网络问题无法收到请求结果而重新发起请求,或是前端的操作抖动而造成重复提交情况。 在交易系统,支付系统这种重复提交造成的问题有尤其明显,比如:

  1. 用户在APP上连续点击了多次提交订单,后台应该只产生一个订单;
  2. 向支付宝发起支付请求,由于网络问题或系统BUG重发,支付宝应该只扣一次钱。 很显然,声明幂等的服务认为,外部调用者会存在多次调用的情况,为了防止外部多次调用对系统数据状态的发生多次改变,将服务设计成幂等。

什么情况下需要保证幂等性

以SQL为例,有下面三种场景,只有第三种场景需要开发人员使用其他策略保证幂等性:

  1. SELECT col1 FROM tab1 WHER col2=2,无论执行多少次都不会改变状态,是天然的幂等。
  2. UPDATE tab1 SET col1=1 WHERE col2=2,无论执行成功多少次状态都是一致的,因此也是幂等操作。
  3. UPDATE tab1 SET col1=col1+1 WHERE col2=2,每次执行的结果都会发生变化,这种不是幂等的。

幂等的不足

幂等是为了简化客户端逻辑处理,却增加了服务提供者的逻辑和成本,是否有必要,需要根据具体场景具体分析,因此除了业务上的特殊要求外,尽量不提供幂等的接口。

  1. 增加了额外控制幂等的业务逻辑,复杂化了业务功能;
  2. 把并行执行的功能改为串行执行,降低了执行效率。

保证幂等策略

幂等需要通过唯一的业务单号来保证。也就是说相同的业务单号,认为是同一笔业务。使用这个唯一的业务单号来确保,后面多次的相同的业务单号的处理逻辑和执行效果是一致的。 下面以支付为例,在不考虑并发的情况下,

实现幂等原理

  1. 先查询一下订单是否已经支付过,
  2. 如果已经支付过,则返回支付成功;
  3. 如果没有支付,进行支付流程,修改订单状态为‘已支付’。

上述的保证幂等方案是分成两步的,第②步依赖第①步的查询结果,无法保证原子性的。在高并发下就会出现下面的情况:第二次请求在第一次请求第②步订单状态还没有修改为‘已支付状态’的情况下到来。既然得出了这个结论,余下的问题也就变得简单:把查询和变更状态操作加锁,将并行操作改为串行操作。

方案1:乐观锁 版本号

方案介绍:

  • 数据库乐观锁方案一般只能适用于执行更新操作的过程,我们可以提前在对应的数据表中多添加一个字段version,充当当前数据的版本标识。
  • 这样每次对该数据库该表的这条数据执行更新时,都会将该版本标识作为一个条件,值为上次待更新数据中的版本标识的值。

在这里插入图片描述

  • 每次查询的时候查询出版本号,每次执行更新的时候就带上这个版本号。
  • 服务端接口收到请求后按照版本号去更新数据,每次更新后将版本号 + 1
  • 如果重复发起请求,那么每次请求的version一定是一样的,但是只要有一次更新成功了那么数据库的版本号就+1,可以保证后面的请求更新数据库不会成功。
优缺点分析:
  • 实现简单,只需要在表中增加一个字段即可。
  • 适用面不广,只能适用于更新相关的场景。
  • 效率不高,适用数据库来保证幂等性,这样就是把压力放到数据库去了,本来数据库就是很多项目的性能瓶颈。

方案2: 防重表 ----唯一索引解决方案

方案介绍:

  • 根据业务需求,对数据库表中的字段设置唯一索引(一般已订单号order_id),可以是单一索引,也可以是联合索引。防止新增脏数据。
  • 如新增用户数据,一般用户名是唯一的。所以会在用户名字段添加唯一索引。 在接口做插入操作的时候,第一次请求时数据会插入成功。
  • 后续重复的数据插入数据时,会抛出异常提示唯一索引有冲突。此时我们需要对该异常进行捕获,然后返回成功,更加直观地反馈给请求端,而不是直接反馈异常,否则终端也不好识别,以此来保证接口的幂等性。

在这里插入图片描述

优化方案:防重表放到redis

把防重表放到redis当中。set的数据结构

在这里插入图片描述

在这里插入图片描述

方案3: Token+Redis 解决方案

Token解决方案适用于新增或者修改操作,这种方案由于不依赖于接口内部代码进行判断,所以可以通过拦截器或AOP切面 + 注解的方式做得更加通用,仅用一个注解就能让某个接口保证幂等性。

  1. 主要分为两个阶段,获取token和使用token。每次接口请求前先获取一个token
  2. 然后再下次请求的时候在请求中加上这个token,后台进行验证,如果验证通过删除token,下次请求再次判断token。

在这里插入图片描述

  1. 服务端需要提供一个token获取接口(该 Token 可以是一个序列号,也可以是一个分布式 ID 或者 UUID
    串,要求保证唯一性),客户端调用接口获取 Token,这时候服务端会生成一个 Token 。
  2. 然后将该串存入 Redis 数据库中,服务端以该 Token 作为 Redis 的键(注意设置过期时间)。
  3. 将 Token 返回到客户端,客户端拿到后应存到表单隐藏域中。
  4. 客户端在执行提交表单时,把 Token 存入到 Headers 中,执行业务请求带上该 Headers。
  5. 服务端接收请求后从 Headers 获取 Token,然后根据 Token 到 Redis 中查找该 key 是否存在。
  6. 服务端根据 Redis 中是否存该 key 进行判断,如果存在就将该 key 删除,然后正常执行业务逻辑。
  7. 如果不存在就抛异常,返回重复提交的错误信息。
    在这里插入图片描述
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、课程介绍 2、解决方案的效果演示(结合支付系统真实应用场景 3、常用的分布式事务解决方案介绍 4、消息发送一致(可靠消息的前提保障) 5、消息发送一致的异常流程处理 6、常规MQ队列消息的处理流程和特点 7、消息重复发送问题及业务接口的设计 8、可靠消息最终一致方案1(本地消息服务) 9、可靠消息最终一致方案2(独立消息服务)的设计 10、可靠消息服务的设计与实现--消息服务子系统 11、可靠消息服务的设计与实现--消息管理子系统 12、可靠消息服务的设计与实现--消息状态确认子系统 13、可靠消息服务的设计与实现--消息恢复子系统 14、可靠消息服务的设计与实现--实时消息服务子系统 15、可靠消息最终一致方案在支付系统中的实战应用介绍 16、可靠消息最终一致方案在支付系统中的实战应用部署 17、可靠消息最终一致方案的实战应用测试 18、可靠消息最终一致方案的优化提升 19、最大努力通知型方案的应用场景介绍 20、最大努力通知型方案的方案设计 21、最大努 力通知型方案的实战应用与部署 22、最大努力通知型方案的优化提升 23、TCC型事务方案介绍 24、TCC型事务架构设计分析 25、TCC型事务框架的源码实现讲解 26、TCC型事务方案的项目实战应用介绍 27、TCC型事务方案的项目实战应用部署 28、TCC型事务方案的项目实战应用测试 29、TCC型事务方案的应用优化提升 30、课程总结

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值