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

2019/04/10 10:10
阅读数 72

抢微信红包的时候我们都知道:一个红包一旦你抢过之后,以后无论你点多少次都是一样的结果。红包会提示你已经抢过此红包,而不会让你再抢一次。

抢红包接口就是一个非常典型的幂等接口,抢一次和抢多次具有一样的效果。类似的接口在我们的开发过程中会有很多,比如在电商的下单过程中:

订单创建接口,第一次调用返回超时了,重试机制一般会再次调用这个接口,此时我们不能因为这个接口被调了两次就创建两个一样的订单; 库存扣减接口,支付接口也是类似的逻辑;

今天的文章就来讲讲什么是接口的幂等性,并介绍几种实现接口幂等性的方案。

什么是幂等

幂等原先是数学中的一个概念,表示进行1次变换和进行N次变换产生的效果相同。

当我们讨论接口的幂等性时一般是在说:以相同的请求调用这个接口一次和调用这个接口多次,对系统产生的影响是相同的。如果一个接口满足这个特性,那么我们就说这个 接口是一个幂等接口。比如上面的抢红包接口。

PS:这边顺带说下幂等和防止重复提交的区别。 防止重复提交更多的是不让用户发起多次一样的请求。比如说用户在线购物下单时点了提交订单按钮,但是由于网络原因响应很慢,此时用户比较心急多次点击了订单提交按钮。 这种情况下就可能会造成多次下单。一般防止重复提交的方案有:将订单按钮置灰,跳转到结果页等。主要还是从客户端的角度来解决这个问题。

幂等更多的是在重复请求已经发生,或是无法避免的情况下,采取一定的技术手段让这些重复请求不给系统带来副作用。

什么情况下需要幂等

并不是所有接口都需要保证幂等性。以相同的请求调用这个接口一次或多次,需要给调用方返回一致的结果时,就要考虑将这个接口设计成幂等接口。

实现幂等的几种方案

在我们设计幂等接口时重点关注新增接口和更新接口。因为查询和删除操作天生是幂等的(根据id查询和根据id删除多次对系统的影响是一致的),不需要我们提供额外的 技术手段来保证幂等性。(??)

对于新增和更新接口,大致有以下几种方案可以保证接口幂等性。

来源加序列号

这是一种比较好理解,通用的方案。

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

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

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

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

唯一索引(唯一字段)

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

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

PS:大家可能会有一个疑问,我同一个请求调用两次,第一返回新增成功,第二次返回失败,返回的结果不同啊。这个接口还是幂等接口么? 这边我要重申下概念,幂等强调的是接口一次调用和多次调用产生的效果是一样的。这边调用一次和调用多次都是新增了一个对象,所以还是满足幂等的。

乐观锁

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

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

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

参考

公众号推荐

欢迎大家关注我的微信公众号,我会把好的博客同步更新到公众号上

原文出处:https://www.cnblogs.com/54chensongxia/p/12598944.html

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部