概念
一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同
。
幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变.
【注】很多人把他理解为“幂等性是指重复使用同样的参数调用同一方法时总能获得同样的结果
”这个是不准确的。
幂等性是系统的接口对外一种承诺(而不是实现), 承诺只要调用接口成功, 外部多次调用对系统的影响是一致的. 声明为幂等的接口会认为外部调用失败是常态, 并且失败之后必然会有重试.
幂等(idempotence)是来自于高等代数中的概念。
单目运算, x为某集合内的任意数, f为运算子如果满足f(x)=f(f(x)), 那么我们称f运算为具有幂等性(idempotent)
比如在实数集中,绝对值运算就是一个例子: abs(a)=abs(abs(a))
双目运算,x为某集合内的任意数, f为运算子如果满足f(x,x)=x, f运算的前提是两个参数都同为x, 那么我们也称f运算为具有幂等性
比如在实数集中,求两个数的最大值的函数: max(x,x) = x, 还有布尔代数中,逻辑运算 “与”, “或” 也都是幂等运算, 因为他们符合AND(0,0) = 0, AND(1,1) = 1, OR(0,0) = 0, OR(1,1) = 1
场景
在微服务架构下,我们在完成一个订单流程时经常遇到下面的场景:
1.一个订单创建接口,第一次调用超时了,然后调用方重试了一次
2.在订单创建时,我们需要去扣减库存,这时接口发生了超时,调用方重试了一次
3.当这笔订单开始支付,在支付请求发出之后,在服务端发生了扣钱操作,接口响应超时了,调用方重试了一次
4.一个订单状态更新接口,调用方连续发送了两个消息,一个是已创建,一个是已付款。但是你先接收到已付款,然后又接收到了已创建
5.在支付完成订单之后,需要发送一条短信,当一台机器接收到短信发送的消息之后,处理较慢。消息中间件又把消息投递给另外一台机器处理
为了解决以上问题,就需要保证接口的幂等性,接口的幂等性实际上就是接口可重复调用,在调用方多次调用的情况下,多次执行所产生的影响均与一次执行的影响相同
。有些接口可以天然的实现幂等性,比如查询接口,对于查询来说,你查询一次和两次,对于系统来说,没有任何影响,查出的结果也是一样。
幂等实现方式
除了查询功能具有天然的幂等性之外,增加、更新、删除都要保证幂等性。那么如何来保证幂等性呢?
幂等实现方式有以下两种:
- 通过业务本身实现幂等性;
- 系统缓存处理结果,检测到重复请求,返回之前处理结果;
全局唯一ID
如果使用全局唯一ID,就是根据业务的操作和内容生成一个全局ID,在执行操作前先根据这个全局唯一ID是否存在,来判断这个操作是否已经执行。如果不存在则把全局ID,存储到存储系统中,比如数据库、redis等。如果存在则表示该方法已经执行。
插入或更新
这种方法插入并且有唯一索引的情况,比如我们要关联商品品类,其中商品的ID和品类的ID可以构成唯一索引,并且在数据表中也增加了唯一索引。这时就可以使用InsertOrUpdate操作。在MySQL数据库中如下:
insert into goods_category (goods_id,category_id,create_time,update_time)
values(#{goodsId},#{categoryId},now(),now())
on DUPLICATE KEY UPDATE
update_time=now()
多版本控制
这种方法适合在更新的场景中,比如我们要更新商品的名字,这时我们就可以在更新的接口中增加一个版本号,来做幂等
@Update("update goods set name=#{newName},version=#{version} where id=#{id} and version<${version}")
boolean updateGoodsName(int id,String newName,int version);
状态机控制
这种方法适合在有状态机流转的情况下,比如就会订单的创建和付款,订单的付款肯定是在之前,这时我们可以通过在设计状态字段时,使用int类型,并且通过值类型的大小来做幂等,比如订单的创建为0,付款成功为100。付款失败为99
在做状态机更新时,我们就这可以这样控制
update `order` set status=#{status} where id=#{id} and status<#{status}
总结
幂等性应该是合格架构师的一个基因,在设计系统时,是设计平台api首要要考虑的问题。提供平台服务的时候也必须标注接口是否幂等,不然调用方遇到异常可能不知道该不该重试。特别是超时的时候,根本没法知道服务的状态。
幂等是一个稳定平台服务接口的必备特性。
参考文档
http://zhizus.com/2017-08-28-%E5%B9%82%E7%AD%89.html
可参考 : https://blog.csdn.net/mine_song/article/details/74725990 多种方式保证幂等