什么是接口幂等性

接口幂等性是指一个接口在被调用一次和被调用多次后的结果是一样的

为什么需要接口幂等性

  1. 用户的重复提交或者用户的恶意攻击
  2. 网络波动导致的超时重传可能会导致接口重复调用
  3. 比如你在购买商品下单成功后需要支付,如果你付了钱但是由于网络波动,你没有收到支付成功,因此你多点击了几下,如果没有幂等性就会导致多次扣钱

常见的重复请求场景

  • 前端表单的重复提交
  • 黑客恶意攻击
  • 接口超时重传
  • 消息队列中的消息重复消费

哪些接口需要幂等

本身幂等的:

  • 查询操作和删除操作不需要幂等保证,因为本身就是幂等
  • 更新操作的赋值操作本身也是幂等,不需要幂等保证

本身不是幂等需要幂等保证:

  • 新增操作
  • 更新操作如果是将某个字段增量更新需要幂等

幂等性如何保证

  1. 数据库唯一性约束,需要生成全局唯一性ID,适用于新增操作
  2. 防重表,要生成全局唯一性ID,以及还要考虑数据量大的情况下的分库分表或者过期数据清理情况,适用于新增操作。好处是如果有两个业务场景,A场景不允许重复,B场景允许重复,通过防重表将唯一键和业务分离开,降低耦合
  3. 数据库乐观锁,通过版本号法,在记录字段上加上version,update的时候需要满足where version = #{version},适用于更新操作
  4. 防重token令牌,适用于更新、新增、删除操作
    1. 客户端会先去向服务端发送一个请求去得到一个token,服务端生成一个token并存在redis中设置ttl
    2. 客户端第二次调用业务请求的时候必须要携带这一个token,然后服务端校验
    3. 检验成功后去查看redis中有没有token,如果有则执行业务并且删除redis中的token,如果没有则说明redis中没有对应的token,表示重复操作

防重token令牌的问题

假设某个客户端第一次发起请求,服务端收到请求后将token从redis中删除,接着去执行业务逻辑。但是业务逻辑执行失败,有两种可能:

  1. 服务端向客户端返回执行失败,客户端收到后会请求重新生成一个token,这里没有幂等性问题
  2. 服务端向客户端返回执行失败,但是由于网络问题导致了丢包,此时客户端会超时重传,但是服务端返回的却是重复请求或者执行成功(有的业务鉴别出重复请求当作执行成功处理)
  3. 因此我们可以结合业务场景,在防重token令牌的基础上,再在db层加上前三种方案作为兜底

可以用分布式锁做幂等性吗

不可以

  1. 如果用于连续发了两次请求,第一次请求先到底去执行,第二次请求由于一些原因过了一会才能执行,如果第一次请求执行完了并且释放锁,第二次请求也拿到了锁,那么不能保证幂等性
  2. 客户端第一次发起请求,服务端执行完成并且释放了锁,但由于网络原因客户端没有收到,于是客户端再次发起请求,但是如果ttl过期了第二次请求也能获取到锁并执行,不能保证幂等性