接口幂等性是个啥

什么是接口幂等?

“幂等”是数学和计算机学中一个概念,常见于抽象代数中。

在数学里,幂等的定义主要有两种:

  • 元素幂等:在某二元运算下,元素自己和自己运算,结果等于自己,那么该元素是幂等元素。比如乘法运算下,幂等实数有两个0和1。
  • 运算幂等:对于某一元运算,对于任意元素的运算结果再次进行运算,两次运算结果一样,即f(f(X))=f(X)f(f(X)) = f(X)

那什么是接口幂等呢?第一次听到这个概念的时候,还感觉挺懵的。其实这里套用的是前面提到的第二种定义“运算幂等”。

所谓“接口幂等”,是指 以相同的请求调用这个接口一次和调用这个接口多次,对系统产生的影响是相同的。如果一个接口满足这个特性,那么我们就说这个接口是一个幂等接口。

举个简单例子:

  1. 抢红包接口:用户可能一直在点,发了很多请求到后端,不能因为发了多次请求,就让用户抢多次
  2. 接口超时: 为了解决接口超时问题,通常会引入了重试机制。第一次请求接口超时了,请求方没能及时获取返回结果(此时有可能已经成功了),为了避免返回错误的结果(这种情况不可能直接返回失败吧?),于是会对该请求重试几次,这样也会产生重复的数据。

因此,为了保证系统的可靠性,我们需要保证某些接口具有幂等性。

那么如何保证接口幂等性呢?我们接着往下看!

如何实现接口幂等?

在设计接口幂等性的时候,我们只需要考虑新增接口和更新接口。因为查询和删除操作天生具有幂等性,因为多次调用这些接口对系统产生的影响和单次调用是一致的。

对于新增和更新接口,保证接口幂等性的可能方案,有如下几种:

  1. 添加序列号

    当调用接口时,参数中必须传入source字段和seq字段(这边举了一个我们项目中的列子,其实并不一定要传两个字段,传一个唯一的序列号uuid也能达到一样的效果)。服务端接收到请求,先判断自己是否是一个幂等接口,如果不是幂等接口就正常处理请求。

    如果是一个幂等接口,就将sourceseq组成联合主键去数据库表中或者是Redis中查询,如果没有查询到,说明没处理过这个请求,然后正常处理请求就行了。处理完之后将处理结果和sourceseq信息一个存入数据库或Redis中。

    如果根据sourceseq能查询到,说明已经处理过这个请求了,直接将处理的结果返回即可。

    这种方案非常简单,而且易于理解,通用。但是如果请求量很大的话,存放请求记录的表会很大,这个时候可以将一段时间之前的记录删除,以提升性能。

  2. 唯一索引

    这种方案适合用于执行新增操作的接口。

    比如说新增用户接口。我们将用户表中的身份证字段加上唯一索引。当同一个请求调用两次时,我们可以先根据身份证字段查询下用户是否存在,不存在的话再新增。存在的话就返回新增失败。
    或者直接新增也行,数据库会抛异常,我们对异常处理返回前台就行了。

  3. token

    对于幂等接口,可以把token放到redis中,后端收到请求后,先判断redis中是否有该token,如果没有说明是第一次调用,否则是第二次调用。其实和前面的“添加序列号”思想差不多。

  4. 乐观锁

    这种方案适用于执行更新操作的接口。

    乐观锁只是在更新数据那一刻锁表,其他时间不锁表,所以相对于悲观锁,效率更高。 我们一般通过数据库来实现乐观锁,比较通用的做法是增加一个时间戳字段。

    1
    update table_xxx set name=#name#, timestamp = now where id=#id# and timestamp=#timestamp# --这个值由前端到数据中查询出来,再传过来
  5. 悲观锁

    适合更新操作的接口。直接把某标数据锁住,同一时刻只允许一个事务更新改数据。通常通过如下sql锁住单行数据:

    1
    select * from user id=123 for update;
  6. 状态机

    很多时候业务表是有状态的,比如订单表中有:1-下单、2-已支付、3-完成、4-撤销等状态。如果这些状态的值是有规律的,按照业务节点正好是从小到大,我们就能通过它来保证接口的幂等性。

    假如id=123的订单状态是已支付,现在要变成完成状态。

    1
    update `order` set status=3 where id=123 and status=2;

    第一次请求时,该订单的状态是已支付,值是2,所以该update语句可以正常更新数据,sql执行结果的影响行数是1,订单状态变成了3

    后面有相同的请求过来,再执行相同的sql时,由于订单状态变成了3,再用status=2作为条件,无法查询出需要更新的数据,所以最终sql执行结果的影响行数是0,即不会真正的更新数据。

参考资料

  1. https://www.cnblogs.com/54chensongxia/p/12598944.html
  2. https://segmentfault.com/a/1190000039737646
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2022 Yin Peng
  • 引擎: Hexo   |  主题:修改自 Ayer
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信