持续学习&持续更新中…
守破离
【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【24】【订单服务】接口幂等性
什么是幂等性
接口幂等性就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用;
比如说支付场景,用户购买了商品支付扣款成功,但是返回结果的时候网络异常,此时钱已经扣了,用户再次点击按钮,此时会进行第二次扣款,返回结果成功,用户查询余额返发现多扣钱了,流水记录也变成了两条...
这就没有保证接口的幂等性。
哪些情况需要防止
- 用户多次点击按钮提交
- 用户回退页面再次提交
- 微服务互相调用,由于网络问题,导致请求失败。feign 触发重试机制
- 其他业务情况
什么情况下需要幂等
以 SQL 为例,有些操作是天然幂等的。
SELECT * FROM table WHER id=?
,无论执行多少次都不会改变状态,是天然的幂等。UPDATE tab1 SET col1=1 WHERE col2=2
,无论执行成功多少次状态都是一致的,也是幂等操作。delete from user where userid=1
,多次操作,结果一样,具备幂等性insert into user(userid,name) values(1,'a')
如果 userid 为唯一主键,即使重复操作上面的业务,也只会插入一条用户数据,具备幂等性。
UPDATE tab1 SET col1=col1+1 WHERE col2=2
,每次执行的结果都会发生变化,不是幂等的。
insert into user(userid,name) values(1,'a')
如果 userid 不是主键,可以重复,那上面业务多次操作,数据都会新增多条,不具备幂等性。
幂等解决方案
token 机制
工作流程:
- 服务端提供了发送 token 的接口。我们在分析业务的时候,哪些业务是存在幂等问题的, 就必须在执行业务前,先去获取 token,服务器会把 token 保存到 redis 中。
- 然后服务器把该 token 返回给页面
- 页面调用业务接口请求时,把 token 携带过去,一般放在请求头部。
- 服务器判断 token 是否存在 redis 中,存在表示第一次请求,然后删除 token,继续执行业务。
- 如果判断 token 不存在 redis 中,就表示是重复操作,直接返回重复标记给 client ,这样就保证了业务代码,不被重复执行。
危险性:
-
先删除 token 还是后删除 token;
- 先删除可能导致,业务确实没有执行(或者没有成功执行),重试还带上之前 token,由于防重设计导致, 请求还是不能执行。
- 后删除可能导致,业务处理成功,但是服务闪断,出现超时,没有删除 token,别 人继续重试,导致业务被执行两遍
- 我们最好设计为先删除 token,如果业务调用失败,刷新页面,重新获取 token ,再次请求。
-
Token 获取、比较和删除必须是原子性
redis.get(token)
、token.equals
、redis.del(token)
如果这些个操作不是原子操作,可能导致高并发下,都 get 到同样的数据,判断都成功,继续业务并发执行- 可以在 redis 使用 lua 脚本完成这个操作
if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1])