项目中,如何防止重复提交(保证接口幂等)

  项目中经常有场景,需要处理重复提交的请求。
  比如下单时卡顿了,用户不停刷新,我们要如何防止多次扣款?同时,我们也需要考虑在分布式环境下的情况。

有如下几种思路

  • 1、前端控制
  • 2、保持接口幂等性
    • 2.1、什么情况需要注意接口幂等
    • 2.2、如何保持接口幂等性
      • (1)使用分布式锁,以请求唯一标识为key【推荐】
      • (2)状态机
  • 3、数据库锁

1、前端控制

  页面上控制按钮点击一次就置灰,不让重复点击。
  这种方案并不是靠保证接口幂等实现的,可靠性不高但简单。

2、保持接口幂等性

2.1、什么情况需要注意接口幂等

  接口幂等性就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用。
  一般而言,查询和删除操作不需要考虑幂等,更新视情况而定,新增需要考虑。
  查询操作天然是幂等的。
  删除操作在不考虑返回结果的情况下,也是幂等的。
  更新操作不一定是幂等的,如果是更新成某个特定值,则是幂等的,比如update t set t.name='张三' where id='1';如果是递增或递减等操作,则是不幂等的,比如update t set t.count=t.count+1 where id = '1'
  新增操作是不幂等的。

2.2、如何保持接口幂等性

(1)使用分布式锁,以请求唯一标识为key【推荐】

  分布式锁可以考虑通过zooKeeper、redission等实现。当收到请求时,以请求的唯一标识来加锁。加锁成功则继续后续业务操作,否则直接返回。

  这里的请求唯一标识可以有如下几种思路
(a)使用临时的token
  即提前先请求一个一次性的token,在请求要保证幂等性的接口时,以此token为唯一标识,该接口对传入的token进行校验。
(b)根据方法名+业务参数为标识
  可以对方法名和业务参数做一个摘要,形成唯一标识,接口对此进行校验。
  需要注意的是,若此处的业务参数中带有时间戳、经纬度等常变参数时,形成摘要时需要剔除。

(2)状态机

  如果业务上可以用状态机来判断的,也可以实现幂等性。
  比如只有未下单状态的订单才能下单。

3、数据库锁

  也可以通过数据库锁来防止数据的重复操作,但也存在局限性。
  比如接口中可能还操作了redis等缓存,只在数据库层面加锁的话,就很可能导致缓存数据不一致。而且既然是重复请求,还是在代码上直接拦截掉比较好,不用将压力给数据库。

你可能感兴趣的