什么是接口幂等性?

幂等性原本是数学上的概念,用在接口上就可以理解为:同一个接口,多次发出同一个请求,必须保证操作只执行一次。 调用接口发生异常并且重复尝试时,总是会造成系统所无法承受的损失,所以必须阻止这种现象的发生。
比如下面这些情况,如果没有实现接口幂等性会有很严重的后果: 支付接口,重复支付会导致多次扣钱 ;订单接口,同一个订单可能会多次创建。
image.png

为什么会产生接口幂等性问题?

那么,什么情况下,会产生接口幂等性的问题呢?

  • 网络波动,可能会引起重复请求;
  • 用户重复操作,用户在操作时候可能会无意触发多次下单交易,甚至没有响应而有意触发多次交易应用;
  • 使用了失效或超时重试机制(Nginx重试、RPC重试或业务层重试等);
  • 页面重复刷新;
  • 使用浏览器后退按钮重复之前的操作,导致重复提交表单;
  • 使用浏览器历史记录重复提交表单;
  • 浏览器重复的HTTP请求;
  • 定时任务重复执行;
  • 用户双击提交按钮;

    如何保证接口幂等性

    解决办法分为两个方向,一个方向是客户端防止重复调用,一个是服务端进行校验。当然,客户端防止重复提交并不是绝对可靠的,优点是实现起来比较简单。

    按钮只可操作一次

    一般是提交后把按钮置灰或loding状态,消除用户因为重复点击而产生的重复记录,比如添加操作,由于点击两次而产生两条记录。

    token机制

    服务端提供发送 token 的接口,业务调用接口前先获取token,然后调用业务接口请求时,把token携带过去,服务器判断token是否存在redis中,存在表示第一次请求,可以继续执行业务,执行业务完成后,最后需要把redis中的token删除。
    接口幂等性 - 图2

    唯一id

    每次操作,都根据操作和内容生成唯一id,在执行之前先判断id是否存在,如果不存在则执行后续操作,并且保存到数据库或者redis等。

    数据库唯一索引

    利用数据库唯一索引机制,当数据重复时,插入数据库会抛出异常,保证不会出现脏数据。

    版本控制(乐观锁)

    如果更新已有数据,可以进行加锁更新,也可以设计表结构时使用乐观锁,通过version来做乐观锁,这样既能保证执行效率,又能保证幂等, 乐观锁的version版本在更新业务数据要自增。

    分布式锁

    如果是分布是系统,构建全局唯一索引比较困难,例如唯一性的字段没法确定,这时候可以引入分布式锁,通过第三方的系统(redis或zookeeper),在业务系统插入数据或者更新数据,获取分布式锁,然后做操作,之后释放锁,这样其实是把多线程并发的锁的思路,引入多多个系统,也就是分布式系统中得解决思路。要点:某个长流程处理过程要求不能并发执行,可以在流程执行之前根据某个标志(用户ID+后缀等)获取分布式锁,其他流程执行时获取锁就会失败,也就是同一时间该流程只能有一个能执行成功,执行完成后,释放分布式锁(分布式锁要第三方系统提供)。
    接口幂等性 - 图3

    状态控制

    例如订单有状态已支付、未支付、支付中、支付失败、当处于未支付的时候才允许修改未支付中等。