分布式接口幂等性

https://www.cnblogs.com/huaixiaonian/p/9577567.html

最近跟朋友聊起这个话题,想深入了解下,于是学习总结,记录下来,此文章参考以下博客综合而来表示感谢:

http://blog.brucefeng.info/post/api-idempotent

http://825635381.iteye.com/blog/2276077

https://www.cnblogs.com/leechenxiang/p/6626629.html

1. 接口调用存在的问题

现如今我们的系统大多拆分为分布式SOA,或者微服务,一套系统中包含了多个子系统服务,而一个子系统服务往往会去调用另一个服务,而服务调用服务无非就是使用RPC通信或者restful,既然是通信,那么就有可能在服务器处理完毕后返回结果的时候挂掉,这个时候用户端发现很久没有反应,那么就会多次点击按钮,这样请求有多次,那么处理数据的结果是否要统一呢?那是肯定的!尤其在支付场景。

2. 什么是接口幂等性

接口幂等性就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用。举个最简单的例子,那就是支付,用户购买商品后支付,支付扣款成功,但是返回结果的时候网络异常,此时钱已经扣了,用户再次点击按钮,此时会进行第二次扣款,返回结果成功,用户查询余额返发现多扣钱了,流水记录也变成了两条...,这就没有保证接口的幂等性

3. 什么情况下需要保证接口的幂等性

在增删改查4个操作中,尤为注意就是增加或者修改,

A: 查询操作

查询对于结果是不会有改变的,查询一次和查询多次,在数据不变的情况下,查询结果是一样的。select是天然的幂等操作

B: 删除操作

删除一次和多次删除都是把数据删除。(注意可能返回结果不一样,删除的数据不存在,返回0,删除的数据多条,返回结果多个,在不考虑返回结果的情况下,删除操作也是具有幂等性的)

C: 更新操作

修改在大多场景下结果一样,但是如果是增量修改是需要保证幂等性的,如下例子:

把表中id为XXX的记录的A字段值设置为1,这种操作不管执行多少次都是幂等的
   把表中id为XXX的记录的A字段值增加1,这种操作就不是幂等的

D: 新增操作

增加在重复提交的场景下会出现幂等性问题,如以上的支付问题

4. 那么如何设计接口才能做到幂等呢?

常见的两种实现方案:  1. 通过代码逻辑判断实现    2. 使用token机制实现      下面以支付系统为例,分别对接口的幂等性进行说明与实现

A: 通过代码逻辑判断实现接口幂等性,只能针对一些满足判断的逻辑实现,具有一定局限性

用户购买商品的订单系统与支付系统;订单系统负责记录用户的购买记录已经订单的流转状态(orderStatus),支付系统用于付款,提供如下接口,订单系统与支付系统通过分布式网络交互。

boolean pay(int accountid,BigDecimal amount) //用于付款,扣除用户的   

这种情况下,支付系统已经扣款,但是订单系统因为网络原因,没有获取到确切的结果,因此订单系统需要重试。由上图可见,支付系统并没有做到接口的幂等性,订单系统第一次调用和第二次调用,用户分别被扣了两次钱,不符合幂等性原则(同一个订单,无论是调用了多少次,用户都只会扣款一次)。如果需要支持幂等性,付款接口需要修改为以下接口:

boolean pay(int orderId,int accountId,BigDecimal amount)

通过orderId来标定订单的唯一性,付款系统只要检测到订单已经支付过,则第二次调用不会扣款而会直接返回结果:

在不同的业务中不同接口需要有不同的幂等性,特别是在分布式系统中,因为网络原因而未能得到确定的结果,往往需要支持接口幂等性。

随着分布式系统及微服务的普及,因为网络原因而导致调用系统未能获取到确切的结果从而导致重试,这就需要被调用系统具有幂等性。例如上文所阐述的支付系统,针对同一个订单保证支付的幂等性,一旦订单的支付状态确定之后,以后的操作都会返回相同的结果,对用户的扣款也只会有一次。这种接口的幂等性,简化到数据层面的操作:

update userAmount set amount = amount - ‘value‘ ,paystatus = ‘paid‘ where orderId= ‘orderid‘ and paystatus = ‘unpay‘

其中value是用户要减少的订单,paystatus代表支付状态,paid代表已经支付,unpay代表未支付,orderid是订单号。

在上文中提到的订单系统,订单具有自己的状态(orderStatus),订单状态存在一定的流转。订单首先有提交(0),付款中(1),付款成功(2),付款失败(3),简化之后其流转路径如图:

当orderStatus = 1 时,其前置状态只能是0,也就是说将orderStatus由0->1 是需要幂等性的
update Order set orderStatus = 1 where OrderId = ‘orderid‘ and orderStatus = 0

当orderStatus 处于0,1两种状态时,对订单执行0->1 的状态流转操作应该是具有幂等性的。这时候需要在执行update操作之前检测orderStatus是否已经=1,如果已经=1则直接返回true即可。

但是如果此时orderStatus = 2,再进行订单状态0->1 时操作就无法成功,但是幂等性是针对同一个请求的,也就是针对同一个requestid保持幂等。

这时候再执行

 update Order set orderStatus = 1 where OrderId = ‘orderid‘ and orderStatus = 0

接口会返回失败,系统没有产生修改,如果再发一次,requestid是相同的,对系统同样没有产生修改。

 

B: 使用token机制实现接口幂等性,通用性强的实现方法

token机制实现步骤:

1. 生成全局唯一的token,token放到redis或jvm内存,token会在页面跳转时获取.存放到pageScope中,支付请求提交先获取token

2. 提交后后台校验token,执行提交逻辑,提交成功同时删除token,生成新的token更新redis ,这样当第一次提交后token更新了,页面再次提交携带的token是已删除的token后台验证会失败不让提交

     token特点:   要申请,一次有效性,可以限流

注意: redis要用删除操作来判断token,删除成功代表token校验通过,如果用select+delete来校验token,存在并发问题,不建议使用

原文地址:https://www.cnblogs.com/Andrew520/p/11407334.html

时间: 2024-10-08 11:07:20

分布式接口幂等性的相关文章

Springboot + redis + 注解 + 拦截器来实现接口幂等性校验

Springboot + redis + 注解 + 拦截器来实现接口幂等性校验 1. SpringBoot 整合篇 2. 手写一套迷你版HTTP服务器 3. 记住:永远不要在MySQL中使用UTF-8 4. Springboot启动原理解析 一.概念 幂等性, 通俗的说就是一个接口, 多次发起同一个请求, 必须保证操作只能执行一次比如: 订单接口, 不能多次创建订单 支付接口, 重复支付同一笔订单只能扣一次钱 支付宝回调接口, 可能会多次回调, 必须处理重复回调 普通表单提交接口, 因为网络超时

保证接口幂等性的解决方案(后台)

假如有个服务提供一个接口(服务部署在多个服务机器),接着有个接口是付款接口.用户在前端上操作的时候,一个订单不小心发起了两次支付请求,然后这两个请求分散在了这个服务部署的不同的机器上,结果一个订单扣款扣两次.这样的场景,就是接口没有保证幂等性的结果. 保证幂等性的核心 1.对于每个请求必须有一个唯一的标识. 2.每次处理完请求之后,必须有一个记录标识这个请求处理过了. 3.每次接收请求需要进行判断之前是否处理过的逻辑处理. 常见解决方案 1.业务表内唯一索引 如果要对创建销售出库单的接口保证幂等

实现接口幂等性的几种方案

抢微信红包的时候我们都知道:一个红包一旦你抢过之后,以后无论你点多少次都是一样的结果.红包会提示你已经抢过此红包,而不会让你再抢一次. 抢红包接口就是一个非常典型的幂等接口,抢一次和抢多次具有一样的效果.类似的接口在我们的开发过程中会有很多,比如在电商的下单过程中: 订单创建接口,第一次调用返回超时了,重试机制一般会再次调用这个接口,此时我们不能因为这个接口被调了两次就创建两个一样的订单: 库存扣减接口,支付接口也是类似的逻辑: 今天的文章就来讲讲什么是接口的幂等性,并介绍几种实现接口幂等性的方

micro-service(3):分布式事务和接口幂等性

分布式服务需要满足CAP原则,Consistency(一致性). Availability(可用性).Partition tolerance(分区容错性),但三者不可得兼:一般都会优先保证可用性和分区容错性,并且保证最终一致性.BASE理论是对CAP原则的补充,Basically Available,Soft state,Eventually Consistent,也就是当CAP三者不能兼得的时候,可以做到最终一致性. 大型应用一般需要做业务分拆以便于提升业务的可维护性和扩展性,但由于业务分布于

高并发下接口幂等性解决方案

一.幂等性概念 在编程中.一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同.幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数.这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变.例如,“getUsername()和setTrue()”函数就是一个幂等函数. 更复杂的操作幂等保证是利用唯一交易号(流水号)实现. 我的理解:幂等就是一个操作,不论执行多少次,产生的效果和返回的结果都是一样的. 二.幂等性场景1.查询操作:查询一次和查询多次,在数

一文讲解高并发下的接口幂等性怎么实现?

实际系统中有很多操作,是不管做多少次,都应该产生一样的效果或返回一样的结果.例如: 前端重复提交选中的数据,应该后台只产生对应这个数据的一个反应结果. 我们发起一笔付款请求,应该只扣用户账户一次钱,当遇到网络重发或系统bug重发,也应该只扣一次钱: 发送消息,也应该只发一次,同样的短信发给用户,用户会哭的: 创建业务订单,一次业务请求只能创建一个,创建多个就会出大问题. 等等很多重要的情况,这些逻辑都需要幂等的特性来支持. 幂等性概念 幂等(idempotent.idempotence)是一个数

(转)关于接口幂等性的总结

前言 元旦放假哪也没去一个人在家里闷得慌,突然间想写点东西打发打发时间,刚好想起前几天在公司听到一些同事在讨论线上数据库出现数据重复的问题,据说是因为接口与前端都没有做重复提交的约束导致的问题,因为我没有参与到相关业务的开发中,所以具体情况不了解,只是听他们在讨论过程中知道一点就是有可能是用户误操作导致接口出现并发问题,还猜测有可能是用户端通过程序脚本的方式来刷接口,虽说后端API使用了Https协议加密传输,但还是有被刷的可能性,也有同学表示说接口要加个同步锁来排重等等.初一听好像没有什么问题

接口幂等性

一.背景 我们实际系统中有很多操作,是不管做多少次,都应该产生一样的效果或返回一样的结果.例如: 1. 前端重复提交选中的数据,应该后台只产生对应这个数据的一个反应结果. 2. 我们发起一笔付款请求,应该只扣用户账户一次钱,当遇到网络重发或系统bug重发,也应该只扣一次钱: 3. 发送消息,也应该只发一次,同样的短信发给用户,用户会哭的: 4. 创建业务订单,一次业务请求只能创建一个,创建多个就会出大问题. 等等很多重要的情况,这些逻辑都需要幂等的特性来支持. 二.幂等性概念 幂等(idempo

API接口幂等性框架设计

表单重复提价问题 rpc远程调用时候 发生网络延迟  可能有重试机制 MQ消费者幂等(保证唯一)一样 解决方案: token 令牌 保证唯一的并且是临时的  过一段时间失效 分布式: redis+token 注意在getToken() 这种方法代码一定要上锁  保证只有一个线程执行  否则会造成token不唯一 步骤 调用接口之前生成对应的 token,存放在redis中 调用接口的时候,将该令牌放到请求头中 (获取请求头中的令牌) 接口获取对应的令牌,如果能够获取该令牌 (将当前令牌删除掉),