接口幂等

274 阅读2分钟

1.定义

接口幂等指的是对同一个操作发起的一次或多次请求的结果是一致的。

2.为什么需要幂等

比如创建订单,如果接口超时,可能导致的结果是订单已经创建成功,但是前端拿到的结果是创单失败,这种情况下再次发起重试创单,就会创建两笔订单,而这并不是用户的意图。

3.场景

  1. 增删改查四种场景下,删除和查询具有天然的幂等性。
  2. 前端重复提交:用户点击多次造成重复提交
  3. 消息重复消费:比如kafka中间件,消息成功消费,但是在ack消息在返回时由于网络抖动等原因,丢失或者晚到,导致producer再次发送消息
  4. 微服务之间的调用,consumer拿到失败的结果,provider其实已经成功

4.实现幂等的方式

4.1 前端

可以通过按钮置灰的方式防止重复提交

4.2 后端

数据库

可以通过uk键,乐观锁(比如version版本号控制)来保证幂等

分布式锁

在业务层面加分布式锁,缺点是会有性能损耗(IO)

Token机制

一些业务没办法通过数据库层面保证幂等(比如不能识别两次下单是否重复下单),分布式锁也会造成性能损耗,可以通过token机制来保证。

token机制简介:token是一种令牌机制,每次请求接口前先去获取一张令牌,然后拿着该令牌请求接口。令牌只能用一次,用过后就销毁。接口通过处理已经用过的令牌(比如不处理该请求)来实现幂等。

token流程图

image.png

token机制的问题

服务端验证token的过程可能会导致错误

a.删除token和业务执行的先后顺序

(a)、先删除可能导致,业务确实没有执行,重试还带上了之前的token,由于防重设计导致,请求还是不能执行; (b)、后删除token问题很大,可能导致,业务处理成功,但是服务闪断,出现超时,没有删除token,别人继续重试,导致业务被执行两次; (c)、我们最好设计为先删除token,如果业务调用失败,就重新获取token再次请求。

b.token的获取、比较和删除必须保证原子性

(a)、如果是通过redis设置和删除token,那么可以redis.get(token)【获取】、token.equals()【比较】、redis.del(token)【删除】、如果这三个操作不是原子的,可能导致高并发下,多个线程都获取到同样的数据,判断都成功,继续业务并发执行; 可以在redis中使用lua脚本完成这个操作,保证上述操作原子性。 (b)、可以将token设置在mysql中,用db事物保证原子性