细说Restful API之幂等性

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了细说Restful API之幂等性相关的知识,希望对你有一定的参考价值。

参考技术A 幂等性原本是数学中的含义,表达的是N次变换与1次变换的结果相同。

而RESTFul API中的幂等性是指调用某个接口1次或N次,对所访问的资源产生的影响结果都是相同的,需要特别注意的是:这里幂等性指的是对资源产生的影响结果,而非调用HTTP请求的返回结果。

举个例子,RESTFul API中的GET方法是查询资源信息,不会对资源产生影响,所以它是符合幂等性的,但是每次调用GET方法返回的结果有可能不同(可能资源的某个属性在调用GET方法之前已经被其他方法修改了,例如在多次访问期间,接口返回对象的update_time字段被别的请求更新,但GET本身是幂等性的)。

实际上,在分布式架构中的API幂等性不仅仅针对RESTFul接口,而是对所有类型的接口适用,目的是为了确保调用1次或N次接口时对资源的影响结果都是相同的。

接口的幂等性确保了无论调用1次还是N次对资源的影响都是相同的,这在某些场合下是非常有用的。

举个业务场景:用户下单,银行从用户账户扣款。

有这样一个接口方法:pay(long account, int money),该方法用于银行卡扣款支付,参数account为账户ID,money为需要扣除的钱数。

当用户从网页上点击支付按钮时,在该方法的实现逻辑中需要从指定账户中扣除对应的商品价钱。如果支付操作已经成功执行,但是响应消息因为某种原因未能及时返回给客户端,这时候给用户的体验是可能是未支付成功,如果此时再次点击支付按钮,那么将再一次执行该方法,结果可能会导致用户只买了一件商品却扣减了双份的钱,这当然是不合理的。整个流程如下图所示:

当然,就上述例子的场景,为了避免用户重复支付,是可以通过别的方式解决的,比如:分布式事务;或者根据支付状态提示给予用户进行提示等等。

但是,如果引入了分布式事务,那么将带来实现上的复杂性,而且会影响到接口性能;而采取提示信息的方式并不能百分之百确保用户不会重复支付,存在一定的风险。

而如果接口符合幂等性,即:对同一个订单无论是执行一次支付还是多次支付,在服务端都确保只会扣一次款,那么既不需要引入分布式事务的复杂性,也能从根本上解决重复支付的问题,这也就是接口符合幂等性的价值所在。

总而言之,接口符合幂等性在可以降低系统实现的复杂性,并能保证资源状态的一致性。

RESTFul风格的接口设计本质上使用的是HTTP协议的请求方法,因此,RESTFul接口方法的幂等性指的就是HTTP方法的幂等性。

常用的HTTP方法有:

那么,这些HTTP方法的幂等性又是什么样的呢?除了幂等性之外,HTTP方法的安全性是指不对资源产生修改。
如下是常用HTTP方法的幂等性和安全性总结:

从上述表格中可以看出,HTTP方法的幂等性和安全性并不是同一个概念,如下是对个各个方法的幂等性和安全性解释:

设计幂等性接口的关键在于保证接口不论是被调用1次还是N次,它对资源所产生的影响都是相同的。
从上述HTTP方法的幂等性总结中可以得知,HTTP协议的POST和PATCH方法都不是幂等性的(但是我们却经常会在RESTFul接口中使用到它们),那是否就意味中无法将POST和PATCH方法设计为幂等性接口了呢?答案显然是否定的。在上述例子中,可以将订单ID也作为方法参数之一,如:pay(long account, int money, long order),这样在服务端确保一个订单只会被支付一次(订单号是全局唯一的),那么无论该方法被调用1次还是N次结果都是一样的,也就保证了接口的幂等性。当然,在哪些没有订单号的场景,可以为接口操作生成一个全局唯一的处理号ID,并把该处理号ID作为方法参数之一,这样在服务端确保一个处理号ID只会被执行一次就保证了接口的幂等性。
符合幂等性的接口调用流程描述如下图所示:

虽然说设计符合幂等性的接口在某些场合可以降低系统的复杂性(如:可以不用引入分布式事务),但是并非在所有场合的问题都能通过幂等性接口解决,在必要的时候依然需要引入分布式事务处理这样的框架。我们不要也不能把接口幂等性作为万能的解决办法,但是,我们在设计接口时尽量考虑符合幂等性处理是非常有价值的。

【参考】

编程思想之幂等性一编程之道

  J前言

  今年年初遇到项目灾难,解决了不少问题,这是其中一个问题。很早的时候写的,学以致用的。今天看到还有这样一篇稿文,那就整理下分享给大家学习!编程思想之幂等性

技术分享图片

  什么是幂等性

  既然幂等性源于数学,那我就使用数学公式来表示,即可一目了然!

  f(f(x)) = f(x)

  显然,从上面的二元函数可以看出,无论x(等幂元素)被函数y无限地执行运算,它的结果都是相同的。在计算机编程领域中,我们可以这么定义幂等性:在调用某个方法、接口中,我们使用相同的参数(相同的特定参数),其返回值都是相同的,我们便可称方法、接口具有幂等性。从信仰上说,幂等性是一种承诺,只要一次答应某个承诺,其承诺内容都是不会改变的。

  Methods can also have the property of “idempotence” in that (aside from error or expiration issues) the side-effects of N 0 identical requests is the same as for a single request.

  如何理解幂等性

  理解性的理论,举例子掌握最快。

  无心举例,看场景更易理解。哈哈

  幂等性场景设计

  下单处理

  这个例子曾经出现在我的身边:微信服务端在搞事情!

  客户端提交数据超过十秒后,他会定时在十秒后自行断开并自动再次发起请求,请求的数据体一模一样,但是这样的请求是不合法的,属重复请求。如何解决此事呢?可以使用幂等性作为一个良好的解决方案。为解决此问题,在此先谢谢腾讯大佬CC,后会无期!

  原本的方法是这样设计的

  function add($userToken, $orderMessage){ //todo}

  这样处理那就不能规避重复请求了。

  基于幂等性来解决此问题,改进的设计方法

  function add($seq,$userToken, $orderMessage){ // 现根据seq来判断是否已经处理过了,是的话就返回第一次处理的结果

  $resultJson = $this-redis-get(‘***_pre_‘ . $seq); if(false != $resultJson){ return $resultJson;

  } // 既然没有处理过,那就正式处理

  // todo

  // 处理完了,没有问题那就将结果保存起来

  // todo

  return $resultJson;

  }

  幂等性属于解决此问题的一部分,是解决方案的一部分,还有另一部分是异步。

  提现

  基于幂等性设计 | 防止用户多次点击(后端是不相信前端处理的)或者突然网络异常等情况下,可以保持数据的一致性。

  两个步骤:

  后端生产票据 | 生产队时给你发粮票,你才有机会拿钱去购买柴米油盐酱醋茶

  根据上一步拿到合法的票据来提现

  function createTicketSequence($user) : string{ // todo }

  function withdraw($ticketSequence,$user,$amount){ // todo}

  场景理解

  1、用户在取款的时候,客户端先带上token请求服务端生成一个合法的取款凭证ticketSequeuence

  2、用户在输入取款金额并确认取款后,客户端将会带上用户登录凭证userToken、取款票据ticketSequence以及取款金额amount进行请求

  3、服务端接收到请求后,先校验userToken,校验失败则返回重新登录,否则换取user对象

  4、用户鉴权通过后,那么再来校验取款票据ticketSequence,票据不合法,那么取款失败,否则继续进行取款,一直到取款成功并根据票据作为幂等值来保存提现成功的结果

  5、即使客户端请求后与服务端失去了联系,并且服务端处理成功,客户端处于假死的状态并再次请求取款,也是返回第一次的结果,并且是迅速的响应。

?

 

以上是关于细说Restful API之幂等性的主要内容,如果未能解决你的问题,请参考以下文章

接口设计之幂等性

编程思想之幂等性一编程之道

kafkaKafka 事务性之幂等性实现

微服务架构之幂等性问题及设计思想,你不得不知的一些幂等方案

高并发核心技术 - 幂等性与分布式锁

如何保证接口的幂等性?常见的实现方案有哪些?