性能文章>什么是幂等,如何对一次请求做出幂等的判断>

什么是幂等,如何对一次请求做出幂等的判断转载

1周前
161100

导语

幂等接口的幂等性实际上就是接口可重复调用,在调用方多次调用的情况下,接口最终得到的结果是一致的。幂等是我们后端性能调优常见的问题,本篇详细的向大家介绍了幂等的调优。

 

正文

为什么需要幂等

分布式场景下,多个业务系统间实现强一致的协议是极其困难的。一个最简单和可实现的假设就是保证最终一致性,这要求服务端在处理一个重复的请求时需要给出相同的回应,同时不会对持久化数据产生副作用(即多次操作与单次操作的结果需要是业务角度一致的)。


一个API拥有幂等能力的话,调用发起方就可以很安全的进行重试。这符合我们普遍的假设。提供幂等能力是服务提供方需要做的事,所以本文是站在服务提供者的角度来写的,即下文的“我们”通常指的是服务提供方。

 

怎么做幂等判断

那么我们怎么对一次请求做出幂等的判断呢?首先我们需要有方法来区分什么是“一次”请求,其次,针对API实现逻辑中持久化数据方式的不同,还需要不同的判断方法。


请求唯一标识

通常我们的API需要增加bizId这样的能够标识请求唯一性的属性,调用发起方使用它来标识一次业务上认为的独特的业务需求。一定要注意它是“业务唯一性标识”,而不是技术唯一性标识,这两者是有本质区别的。

 

事实1:不过大多数情况下,调用发起方可能并不太知道应该如何生成一个有效的请求唯一标识,这需要我们在业务对接的过程中有更多的交流。


需要幂等多久

能够幂等代表我们肯定做了某些数据的持久化,但是任何数据都不可能永久存在,都会要求有一个有效期。

 

对于大多数的请求,建议幂等的有效期是三个月。一些特殊的场景甚至可以是一年。但是几乎不需要更久了。
    

有效期是一个明确的契约,这代表我们可以定期对持久化数据做一些数据治理的工作,同时,超过有效期的请求基本上会幂等失败,那么它的后果就是“同样的事,在几个月后又做了一次”。

 

写入型

如果我们的业务逻辑是在持久化存储中写入什么,那么最好的做法是增加一个unique key,它通常由 user_id, 操作类型, biz_id组成。操作类型是我们系统设计上自行定义的业务操作类型,加上它是为了避免不同的操作类型之间互相干扰。加上user_id是因为通常情况下我们都会分库分表。

 

针对既会写入,还会更新数据的场景,需要把插入放到前面,不然幂等很可能无法工作。比如如下的库存操作的场景,如果两条SQL倒过来写,在库存售磬的场景下就无法幂等了:

 

insert 库存扣减流水;
update 库存 set 库存可用数=库存可用数-1 where 库存id=? and 库存可用数>0;

 

在unique key冲突时,我们需要捕获它,这大概率是幂等了。为什么说是大概率而不是一定呢?这是因为前述提前的“事实1”。调用方很可能错误的重用了请求唯一号,并且可能在重试时就一些核心的参数进行了改动。

 

所以,我们需要在发生unique key冲突时做如下的事情:

1、根据唯一键,将之前写入的数据查询出来。

2、进行关键信息的核对。比如我们提供的API是发放优惠券,那么我们需要至少校验这些信息:接收用户Id,券模板,券面额,有效期等。在关键信息不一致时,返回类似于“DUPLICATE_BUT_DIFFERENT_REQUEST”的错误码;在校验通过时,返回发送成功的券Id。这非常重要,不进行关键信息的校验就返回幂等成功是大多数场景下故障的根源。
更新型

    
更新型最大的挑战在于我们没有地方来存储对于一条数据的多次更新行为,所以大多数情况下需要使用状态机来推测某个更新行为是否发生过了,更复杂的情况可能需要我们增加专用的幂等表。

 

使用状态机

业务上的数据处理大多都是有状态的,比如交易订单。这个时候首选的方法是通过状态机的序列来判断某个更新行为是否已经做过了。不过这并不简单,我们需要非常小心的去看业务上的各种规则和限制,同时,除了更新状态之外,大多数情况下还会伴随着更新一些别的附加信息,我们还需要去检查这些附加信息是否如请求要求的那样更新过了。

 

增加幂等表

通过增加幂等表,就把更新型转化为了2.3节描述的写入型。不过需要注意幂等表需要和当前更新的表在同一个事务中,不然就是无效的。


幂等表需要增加表明数据写入/更新时间的字段,这便于我们定期进行过期数据的清理。

 

删除型

建议在存储上使用逻辑删除而不是物理删除,这样我们就有办法判断删除操作是否已经做过了。删除往往意味着数据进行终态,所以在幂等上也是最好处理的;如果业务的设计上删除不是数据的终态,那么就需要相当小心,因为这违反了通用的设计原则。

 

更多思考

幂等可以说是分布式应用的基石,如你所见,实现它并不像大多数人一开始想像的那么简单。做好它需要我们站在业务语义的角度来设计与思考。 大家可以阅读以下内容加深对幂等知识的学习

  

架构与思维:高并发下幂等性解决方案

分类:
标签:
请先登录,再评论

暂无回复,快来写下第一个回复吧~

为你推荐

从一起GC血案谈到反射原理
前言 首先回答一下提问者的问题。这主要是由于存在大量反射而产生的临时类加载器和 ASM 临时生成的类,这些类会被保留在 Metaspace,一旦 Metaspace 即将满的时候,就会触发 Fu
类初始化导致死锁
一张图简单描述死锁 如上图,Thread1 拿到了 object1,Thread2 拿到了 object2,但是现在 Thread1 需要拿到 object2 的锁才能继续往下,Thread2 又要拿到 object1 才能继续往下
在调试器里看LINUX内核态栈溢出
图灵最先发明了栈,但没有给它取名字。德国人鲍尔也“发明”了栈,取名叫酒窖。澳大利亚人汉布林也“发明”了栈,取名叫弹夹。1959年,戴克斯特拉在度假时想到了Stack这个名字,后来被广泛使用。
不起眼,但是足以让你收获的JVM内存案例
今天的这个案例我觉得应该会让你涨姿势吧,不管你对JVM有多熟悉,看到这篇文章,应该还是会有点小惊讶的,不过我觉得这个案例我分享出来,是想表达不管多么奇怪的现象请一定要追究下去,会让你慢慢变得强大起来,
谨防JDK8重复类定义造成的内存泄漏
概述 如今JDK8成了主流,大家都紧锣密鼓地进行着升级,享受着JDK8带来的各种便利,然而有时候升级并没有那么顺利?比如说今天要说的这个问题。我们都知道JDK8在内存模型上最大的改变是,放弃了Perm
抢了个票,还以为发现了12306的系统BUG
很多人不想多关注业务场景,觉得和技术关系不大,但是,Coder的技术之路却认为:任何技术的落地和实现,都需要依托于真实的业务场景,只有更深入的理解了业务逻辑,才能更好的构建更加合理、稳定、可持续的技术架构。
接口超时,到底如何处理
导语:当前互联网的系统几乎都是解耦隔离后,会存在各个不同系统的相互远程调用。调用远程服务会有三个状态:成功,失败,或者超时。前两者都是明确的状态,而超时则是未知状态。我们转账超时的时候,如果下游转账系统做好幂等控制,我们发起重试,那即可以保证转账正常进行,又可以保证不会多转一笔。所以掌握幂的用法非
什么是幂等,如何对一次请求做出幂等的判断
导语幂等接口的幂等性实际上就是接口可重复调用,在调用方多次调用的情况下,接口最终得到的结果是一致的。幂等是我们后端性能调优常见的问题,本篇详细的向大家介绍了幂等的调优。 正文为什么需要幂等分布式场景下,多个业务系统间实现强一致的协议是极其困难的。一个最简单和可实现的假设就是保证最