面试官:聊一聊接口的幂等性
在日常开发接口的过程中,接口的幂等性问题是我们必须要考虑的,否则会带来很严重的后果。比如在支付场景中,用户不小心点了两次,然后就发现被扣了两次钱,这显然是很严重的问题。因此考虑接口的幂等性是很重要的。
1. 什么是接口幂等性
维基百科解释:
幂等(idempotent、idempotence)是一个数学与计算机学概念,常见于抽象代数中。
在编程中一个幂等操作的特点是其任意多次执行所产生的影响钧与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成高边。例如,"setTrue()"函数就是一个幂等函数,无论多次执行,其结果都是一样的。
通俗解释:
接口幂等性就是用户对同一接口发起了一次或多次请求之后,对数据的影响是不变的,不会因为多次请求而产生不同的结果。
2.什么情况下要考虑接口幂等性问题
select查询操作具有天然幂等性
每次查询不会对数据产生影响。
insert插入操作时
业务主键具有唯一性,因此重复插入也只会有一条数据成功。
重复插入会有多条记录被插入
自增主键,没有幂等性
业务主键,具有幂等性
delete删除操作时
绝对删除,具有幂等性
# 不管执行多少次,都是删除这一条
delete from user where id = 1
相对删除,不具有幂等性
# 范围删除,每次操作,对结果产生的影响都可能不一样,所以不具有幂等性
delete from user where id > 5
update更新操作时,和删除操作同理
绝对更新,具有幂等性
# 这条sql不管执行多少次,对结果的影响都是一样的。
update user set username='尚硅谷' where id = 1
相对更新,不具有幂等性
# 这条sql每次执行,对结果的影响可能都不相同,所以不具有幂等性
update user set username='尚硅谷' where id > 5
3. 幂等的解决方案
对于和Web段交互的接口,我们可以在前端拦截一部分,例如防止表单重复提交,按钮置灰,隐藏,不可点击等。但是稍微懂点技术的人都知道,可以直接去调你的后台接口,所以前端的这种方式只能过滤掉一些普通用户的幂等操作。
那么后端应该怎么处理呢?主要可以通过以下几个方面进行考虑:
3.1 Redis 实现幂等
用户下单支付时,不小心点了两次,这时如果没有做幂等性处理,后台就会收到多次请求,从而会产生两次扣款。
用户发送请求到服务端
尝试用SETNX 指令设置key 和 value,因为SETNX只能放置成功不存在的 key
放置成功,设置Key的过期时间,执行业务逻辑,返回结果
放置失败,返回失败结果
3.2 状态机实现幂等
比如电商的订单,订单支付状态 0-待支付 1-支付中 2-支付成功 3-支付失败
设计的时候,状态一般都是单向改变的,也就是一个订单的状态变化状态是 0 --> 1 --> 2 或者 0--> 1 --> 3
也就是说1-支付中的前置状态必须是0-待支付,2-支付成功的前置状态必须是1-支付中,这样我们在更新的时候就可以加上比较条件,判断当前要更新的订单前置状态是不是我们规定的前置状态,如果不是,就不会更新了,这样多次调用也只会执行一次。
支付订单时,加上判断条件,判断当前这个订单的状态是不是支付中的前置状态 0-待支付
update order set status = 1 where status= 0 and order_id='88888888'
状态枚举
public enum OrderStatusEnum {
UN_PAYMENT(0,0,"待支付"),
PAYING(0,1,"支付中"),
PAY_SUCCESS(1,2,"支付成功"),
PAY_FAIL(0,3,"支付失败"),
;
//前置状态
private int preStatus;
//当前状态值
private int status;
//状态描述
private String desc;
OrderStatusEnum(int preStatus, int status, String desc) {
this.preStatus = preStatus;
this.status = status;
this.desc = desc;
}
}
3.3 数据库去重表
这种方式是利用mysql唯一索引的特性。
处理流程为:
1.建立一张去重表,为其中一个或多个字段建立唯一索引
2.客户端请求服务端,服务端将请求中的一些信息插入去重表
3.因为表中建立了唯一索引,
a.如果插入成功,表示是第一次请求,则可以继续执行后面的业务逻辑。
b.如果插入失败,表示已经执行过当前请求,直接返回。
流程图如下
一般在实际使用过程中,插入操作需要加上事务,如果业务逻辑出现错误执行失败,防重表中数据需要回滚。
3.4 防重Token令牌
对于客户端的重复点击,或者超时重试的情况,也可以用 Token机制实现防止重复提交。简单来说,就是调用方在调用接口之前,先向后端发请求获取一个全局唯一的ID(Token),请求的时候要携带这个Token(一般是放在Headers中),后端要对这个Token进行校验,以Token作为Key,用户信息作为Value到Redis中进行键值对内容比较,如果Key存在且Value匹配就执行删除命令,然后正常执行后面的业务逻辑。如果不存在对应的Key或Value不匹配,就返回重复执行的信息,这样就可以保证幂等性操作。
整体的处理流程:
1.客户端先向服务端请求获取token,服务端返回token,并将token作为Key,用户信息作为Value存入Redis中(注意设置过期时间)
2.客户端请求业务接口,并携带token
3.服务端接收到请求后对token进行校验,token作为Key,在Redis中查询信息,并进行校验
a.如果Redis中存在Key,对比Value,如果匹配,就删除这个Key,并执行业务逻辑
b.如果查出来的Value值不匹配,或者不存在这个Key,则直接返回重复消费的提示信息
流程图:
#村超观赛团#注意,在并发情况下,执行 Redis 查找数据与删除需要保证原子性,否则很可能在并发下无法保证幂等性。其实现方法可以使用分布式锁或者使用 Lua 脚本来注销查询与删除操作。
4.总结
幂等性是开发中一个很常见,也是很重要的问题,尤其是在支付这种与钱相关的业务,保证幂等性是非常重要的,实现幂等性首先要理解业务需求,根据业务需求来确定用哪种方式实现幂等性才比较合理。
举报/反馈
上一篇:暑期去哪儿?在家打游戏看电视不如来蓝雨学画画!
下一篇:准高一新生,怎样利用暑假快人一步呢?做好每一科的规划很重要!
最近更新素质教育
- 信用信息赋能,助力社会信用体系建设
- 今天距第二届全国博士后创新创业大赛总决赛还有10天!
- 北京自学考试和成教有什么不同?
- 道县:开展2023年基础教育业务工作培训
- 融入智能技术 赋能创新人才培养
- 怎么知道自己是不是脊柱侧弯?一个方法教你判断!
- 让长三角市民共享,上海城市业余联赛市面越做越大
- 华容县实验小学:最美莫过夕阳红 最浓不过敬老情
- 原创恭喜!王楚钦代替樊振东世界排名来到第一位,双方积分差仅有15分
- 自考会计专业有哪些课程?
- 播出预告丨上海市天山初级中学:不一样的学习评价
- 杭州亚残运会火炬传递圆满成功 “桂冠”画出现代版“富春山居图”
- 自考学士学位英语什么时候报名
- 日本学校要求的托福分数
- 证明差“老师”能教出好“学生”?江苏博士最新研究成果,引发国际关注!
- 让共同发展繁荣之路越走越宽广
- 原创拒绝恩比德!尼克斯目标清晰!锡伯杜点名要他,三分王成球队答案
- 喜庆乔迁五周年,共度九九重阳节
- 蚂蚁庄园今日答案最新:味精加热太久真的会致癌吗
- 2024年辽宁省普通高考报名即将开始
- 粤港澳大湾区第三届职业技能大赛将于10月23
- 难忘那次5天5夜的长途机动,还有那个同车的义务兵驾驶员
- “周五课堂”来了新老师
- 在学生中走俏的萝卜刀引发争议,消保委提醒:警惕其暴力暗示
- 校园圈丨让学生走出家门做历史作业,结果出人意料