幂等提交
背景
在业务开发中,我们常会面对防止重复请求的问题。当服务端对于请求的响应涉及数据的修改,或状态的变更时,可能会造成极大的危害。特别是交易系统,售后维权,以及支付系统中尤其严重。前台操作的抖动,快速操作,网络通信或者后端响应慢,都会增加后端重复处理的概率。
当然,前端能屏蔽掉重复请求(真的可以吗?前端同学回答下)你也无法逃避 timeout 这个噩梦,所以,既然重复提交无法避免,那我们就从其他地方入手,先看下一些简单的需求场景:
- 前端重复提交选中的数据,应该后台只产生对应这个数据的一个反应结果;
- 我们发起一笔付款请求,应该只扣用户账户一次钱,当遇到网络重发或系统bug重发,也应该只扣一次钱;
- 发送消息,也应该只发一次,同样的短信发给用户,用户会哭的;
既然调用方不靠谱,网络不靠谱,那么接口提供方就必须靠谱,允许你多次调用,我的接口都能handle,这种能力就叫幂等性。
幂等接口
幂等是一个数学概念(摘自维基百科)
- 在某二元运算下,幂等元素是指被自己重复运算(或对于函数是为复合)的结果等于它自己的元素。例如,乘法下唯一两个幂等实数为0和1。
- 某一元运算为幂等的时,其作用在任一元素两次后会和其作用一次的结果相同。例如,高斯符号便是幂等的。
- 一元运算的定义是二元运算定义的特例。
相应地,在计算机领域,如果一个接口一次调用或多次调用的结果是一样的,那么这个接口就是幂等的。但是我没看到一个很严谨的解释,毕竟是延伸过来的说法。
所以还是列举下一些常见的幂等接口:
- HTTP GET
- SQL SELECT
- 大家补充…
不是幂等的接口
- HTTP POST
- SQL UPDATE
- 大家补充…
从这些简单的例子,我大胆归结下计算机领域的幂等性是:
- 一次调用或多次调用对系统影响是一样的
- 接口返回的结果也是一样的(不受接口调用之外的影响)
好了,说完接口的幂等性,它能防止重复提交,接下来讲讲一些经典的实现方案及适用场景。
token校验
由 server 产生,并且存一份,再埋在form/cookies/header;
client 下次请求带上 token, server 校验通过后刷新 token,执行业务;
优点:client 改动很小甚至不需要改动
缺点:无法支持 client 并发请求
其他讨论:server如何存储token/如果用缓存如何设置key
唯一索引
通过把并发问题变成串行问题,如何实现很简单
- 数据库去重表
- redis 原子性
- MQ
加锁
乐观锁
悲观锁
具体哪种看业务对更新成本及更新频率的考量。
状态机幂等
当业务涉及到多个状态更改,如下单支付,广告创建,可以给每一步递增标记,只允许提交大于当前标记的请求,然后每一步的幂等由上诉几种方案实现。
总结
- 防止重复提交跟保障一致性相似,要看场景,没必要强求。
- 幂等性应该列入程序员必备知识,特别是做 web 开发的程序员。
参考
深入理解乐观锁与悲观锁
高并发的核心技术-幂等的实现方案
系统幂等以及常用实现方式
防重复请求处理的实践与总结
分布式高并发系统如何保证对外接口的幂等性
幂等策略分析