如何处理接口幂等性问题(重复提交)
接口幂等:多次请求,结果一致。
同样的请求参数,多次去访问同一个接口,得到的结果是一致的。且服务端(针对于数据入库或数据修改)只处理一次。通俗点讲就是:防止重复提交。
以下演示相关案例
- 案例1: 数据表添加数据
- 案例2:完成任务,领取奖励
- 案例3:修改了其他线程已经修改的数据
- 案例4:接口回调,且存在重试
- 总结
案例1: 数据表添加数据
往数据库添加数据
如下数据库(user),和服务端添加(user)数据接口。
假如用同样的参数去调用添加(user)接口,那么每次都会往数据库插入一条数据,那么数据库就会有多条重复的数据。但是该数据库(user)并没有指明某个字段是唯一的,所以对于服务端来说,每一次请求都是合理的(即使是相同的参数)。 在没有指明字段值唯一时,服务端添加数据接口,应认为所有请求都是合理的,所以不存在幂等性问题。
假如将 user表中的 user_name
字段设定为不能重复
。那么如何去处理幂等性呢?
- 添加数据入库之前,查询
user_name
是否存在。 - 数据表(user)的
user_name
设置唯一索引。推荐
1:添加数据入库之前,查询user_name
是否存在。
以上代码可以解决吗?可以,但是必须是建立在单线程,或没有并发的情况。
为什么必须建立在单线程基础上,或没有并发的情况呢?
假设有多个请求同时到达添加用户接口(save),又同时去数据库中查询(user_name)是否存在,如果user_name不存在,则就会同时添加多条数据,造成(user_name)重复。
如何在以上代码上改进解决?在接口方法上加synchronized
或者使用Lock
锁。
这种方案一定不可取,这会导致该接口变成单线程。同时也存在一个的问题,那就是服务器集群环境下,锁只针对当前服务器,如果多个线程分发给多个服务器去处理,那么同样又重复数据。
那么分布式锁可以解决吗?可以的
注意:如果非要用分布式锁解决的话,一定要将锁的粒度细化,用
user_name
作为锁的key。
2:数据表(user)的user_name
设置唯一索引。推荐
代码修改成以下这样
为什么要将user_name
设置唯一索引
在最终数据持久化(数据库)中,能够非常好的保证数据唯一,同时也能在编码中减少许多代码判断。
案例2:完成任务,领取奖励
相信有不少朋友做过完成活动,然后领取积分,红包,啥的,不知道有没有碰到过多次领取,都领取成功的情况。
假设现在有个需求:用户完成任务,领取1
元现金红包
- 用户在任务列表领取任务,领取后状态是
待完成
。 - 用户完成任务后,状态是
领取奖励
。 - 用户领取完奖励后,状态是
已领取
。
现在要实现的是:
- 用户领取任务,只能领取一次,且重复领取 数据记录表中只能有一条记录。
- 用户完成任务后,领取奖励,重复领取情况下,只能发放奖励一次。
先建立表,以下表揭是模拟表
任务表:
用户任务记录表:
1:用户领取任务,只能领取一次,且重复领取 数据记录表中只能有一条记录
这个实现方式与案例1
一致,需求可以得知 用户id
和 任务id
在用户任务记录表中
是唯一的,所以可以设置一个组合字段的唯一索引。
领取任务的先查询下任务是否领取,然后再添加记录。
2:用户完成任务后,领取奖励,重复领取情况下,只能发放奖励一次
- 查询任务状态是否
已完成
或者已领取
。 - 发放奖励前,采用用户id+任务id组合获取分布式锁。
- 检查下数据表,任务状态是否
已领取
,双重检查。如果不想双重检查可以在接口处获取分布式锁。重要 - 修改任务状态为
已领取
。重要 - 发放奖励后,必须写入至
用户奖励流水表
,并备注因何获得。重要
伪代码 就不贴了。
为什么要使用分布式锁?
因为是涉及的金额之类的,如果出错,就会导致财产损失,以后对账都不好对。加了分布式锁之后,就会变成单线程去执行发放奖励,且锁的粒度也小,不会导致整个接口变成单线程。
前端可以控制重复提交吗? 可以
用户在点击
领取奖励按钮
后,在未返回结果的情况下,按钮不可再点击。
前端控制了重复提交,服务端可以不用去控制吗? 不可以
服务端是整个数据添加的入口,前端只是调用服务端接口的一个应用而已。前端控制了,还有其他渠道也可以访问服务端接口。
案例3:修改了其他线程已经修改的数据
说明:
- 线程A 读取了数据库一行数据。
- 线程B 读取了线程A同一行数据。
- 线程A 把数据修改了。
- 线程B 把数据也修改了,或者改回去了。
- 导致线程A 白忙活了。
此问题我并未有遇到过实际情况,所以不好举例。
因为是修改数据,我都认为每个线程都是合理的,最终数据以最后修改为准。
如何解决:数据表中 每行数据版本
机制
- 线程A 读取到了数据 假设当前行数据的版本是 version = 1。
- 线程B 读取的数据同线程A一致。
- 线程A 修改数据的Sql = set version = version + 1 where version = 1。
- 线程B 修改数据的Sql = set version = version + 1 where version = 1。
- 因为两条Sql的 where条件都是 version = 1,所以只有一个线程修改成功。谁先修改,谁成功。
案例4:接口回调,且存在重试
做过三方支付,或者提供接口给别人对接,应该都知道这个幂等性问题。
举个例子:
假如用户支付成功后,就加相应的积分。用三方支付,支付完后,都会被三方进行回调,假如三方回调我们的接口,出错了,或超时了,三方会继续重试,那就会导致多次调用接口。这也是重复提交。
伪代码:假设我们的服务器应该某些原因,没有及时响应给三方调用者,那么就会触发重试,然后又给用户增加了积分。
如何改进与设计
- 获取此次接口请求的订单号,这是唯一的。
- 查询该订单是否已经处理,如果处理了,响应成功。
- 记录该订单号已经处理了。
- 异步去处理给用户添加积分。
- 响应给三方成功。
伪代码
为什么已经处理该订单 也要响应成功
这可以说是幂等性的核心理念吧,多次请求,结果一致,响应结果也是一致。
总结
幂等性就是在对一些唯一的数据,做校验,当服务器多次接收到这些唯一数据时,做合适的处理。但是响应是同样的结果,都是成功。
重点就是在唯一的数据上做处理。
如何处理接口幂等性问题(重复提交)相关推荐
- php接口防止app重复提交,AOP防止接口重复提交
实现原理 通过自定义注解标记哪些接口需要防范重复提交问题,并定义保持时间: 在Aspect中定义切点,织入所有被自定义注解标记的方法: 在Aspect中定义通知方法,通过PointCut获取类全名.被 ...
- SpringBoot+Redis防止接口重复提交
前言 在项目的使用使用过程中,经常会出现某些操作在短时间内频繁提交.例如:用户鼠标点击过快而重复保存,从而创建了2笔一模一样的单据.针对类似情况,我们就可以全局地控制接口不允许重复提交. 实现思路 创 ...
- 分布式专题——接口幂等性实战
文章目录 1.接口设计与重试机制引起的问题. 2.什么是幂等性? 3.什么情况需要幂等性? 4.业务场景? 5.幂等性设计的核心思想. 6.如何实现接口幂等? 7.select,delete,inse ...
- 架构设计 | 接口幂等性原则,防重复提交Token管理
本文源码:GitHub·点这里 || GitEE·点这里 一.幂等性概念 1.幂等简介 编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同.就是说,一次和多次请求某一个资源会产 ...
- 处理接口超时_架构设计 | 接口幂等性原则,防重复提交Token管理
一.幂等性概念 1.幂等简介 编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同.就是说,一次和多次请求某一个资源会产生同样的作用影响. 2.HTTP请求 遵循Http协议的请 ...
- SpringBoot自定义注解+AOP+redis实现防接口幂等性重复提交,从概念到实战
一.前言 在面试中,经常会有一道经典面试题,那就是:怎么防止接口重复提交? 小编也是背过的,好几种方式,但是一直没有实战过,做多了管理系统,发现这个事情真的没有过多的重视. 最近在测试过程中,发现了多 ...
- 幂等性的研究及后台验证短时间内同一申请是否重复提交的方案
幂等性的研究及实例应用 引入: 这段时间在做新渠道的接入,把以前的核心拿过来copy一份进行改造,在进行代码重读的时候,发现了一个好玩的东西,在申请入件的时候,需要经过一步校验,注释上写的是,对于短时 ...
- SpringBoot 自定义注解+AOP+Redis 防止接口重复提交表单数据
SpringBoot结合Redis处理重复提交 数据重复提交导致多次请求服务.入库,产生脏数据.冗余数据等情况.禁止重复提交使我们保证数据准确性及安全性的必要操作. 实际上,造成这种情况的场景不少: ...
- 接口重复提交解决方案
接口重复提交解决方案 参考文章: (1)接口重复提交解决方案 (2)https://www.cnblogs.com/java-le/p/11056635.html (3)https://www.cod ...
最新文章
- python正则表达式\s \S \w \W区别
- Loadrunner 8.1 下载
- android can为啥能发收不到数据_拼多多登录时手机收不到短信验证码怎么办
- 【大话数据结构算法】快速排序算法
- error: not found: value SparkSession
- Azure Blob Storage 基本用法 -- Azure Storage 之 Blob
- 隹悦服务器批量控制软件
- 【项目分析】利用C#改写JAVA中的Base64.DecodeBase64以及Inflater解码
- mysql2008优化_SQL server 2008 数据库优化常用脚本
- 百度——LBS.云 v2.0——云存储的POI创建和删除--Android 源码
- android 带边框的arc,极细边框(1px边框)实现方式
- android camera 竖直拍照 获取竖直方向照片 做缩放处理
- Java_写出java MyJava abc cde efg 运行的结果(向main()方法中传参)
- 常见高斯型求积公式简介
- 2019我依旧满腔热血,追寻爱与梦想
- 软件测试-软件测试总结
- 帝国cms发布文章对哪些数据表做了操作
- MySQL8.0 - 新特性 - Descending Index 1
- chrome js 读取文件夹_javascript – 如何从chrome扩展程序读取文件?
- Android 获取图片尺寸大小的方法