跳至主要內容

接口幂等

微信公众号:储凡About 15 min

接口幂等

为了防止上述情况的发生,我们需要提供一个防护措施,对于同一笔支付信息如果我其中某一次处理成功了, 我虽然又接收到了消息,但是这时我不处理了,即保证接口的幂等性

幂等是一个数学与计算机学概念,常见于抽象代数中。在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。 幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。

例如,setTrue()函数就是一个幂等函数,无论多次执行,其结果都是一样的,更复杂的操作幂等保证是利用唯一交易号(流水号)实现。

幂等性的核心特点:任意多次执行所产生的影响均与一次执行的影响相同

对于业务中需要考虑幂等性的地方一般都是接口的重复请求,重复请求是指同一个请求因为某些原因被多次提交。导致这个情况会有几种场景:

  • 前端重复提交:提交订单,用户快速重复点击多次,造成后端生成多个内容重复的订单。
  • 接口超时重试:对于给第三方调用的接口,为了防止网络抖动或其他原因造成请求丢失,这样的接口一般都会设计成超时重试多次。
  • 消息重复消费:MQ消息中间件,消息重复消费。

幂等性实现方式

前端处理:

对于和web端交互的接口,我们可以在前端拦截一部分,例如防止表单重复提交,按钮置灰、隐藏、不可点击等方式。

后端处理(重要):

  • Token机制:前端重复连续多次点击的情况,请求前先获取token
  • 数据库去重表:利用唯一索引---->系统容错性不高,瘫痪
  • Redis实现: 过期唯一key
  • 状态机:假设当前状态是已支付,如果支付接口又接收到了支付请求,则会抛异常或拒绝此次处理。

什么情况下需要保证接口的幂等性

在增删改查4个操作中,特别注意增加操作和修改修改:

  • 查询操作:查询对于结果是不会有改变的,查询一次和查询多次,在数据不变的情况下,查询结果是一样的。select操作是天然的幂等操作
  • 删除操作:删除一次和多次删除都是把数据删除。注意:可能返回结果不一样,删除的数据不存在,返回0,删除的数据多条 , 返回结果多个,在不考虑返回结果的情况下,删除操作也是具有幂等性的
  • 更新操作:修改在大多场景下结果一样,但是如果是增量修改是需要保证幂等性的,例如:
    • 把表中idXXX的记录的A字段值设置为1,这种操作不管执行多少次都是幂等的
    • 把表中idXXX的记录的A字段值增加1,这种操作就不是幂等的
  • 新增操作:增加在重复提交的场景下会出现幂等性问题,例如:支付时重复请求问题

为什么需要幂等

幂等性是系统的接口对外一种承诺(而不是实现),承诺只要调用接口成功,外部多次调用(接口一致、参数一致)对系统的影响是一致的。

幂等是为了保证重试机制不会带来数据重复,数据不一致等异常情况。不管在单机还是分布式系统中,都存在因为:

  • 网络抖动无法收到成功应答;
  • 重试补偿机机
  • 用户无意识点击多次发起请求(业务意义上其实是同一条数据记录)

等情况带来的相同参数、多次调用相同接口的情况。

如何做到幂等

  • 有些接口本身就是幂等的,例如:

    • 查询接口,在查询数据没有做变更的情况下,查询操作是幂等的
    • 删除操作一般会带有待删除数据的唯一标识,最终结果都是删除,也是幂等的
  • 对于创建新数据的情况,采用唯一业务单号,业务上的唯一条件约束。

  • 在执行更新操作的时候,可以先查再决定是否更新,但这因为是非原子操作,所以在分布式系统中会存在问题。

    • 多版本控制,更新操作时增加版本条件进行更新,例如: update t_xxx set name=? where version< ?
    • 状态机控制,增加状态字段,状态可能是有先后顺序,例如:订单状态总是从待付款到已付款。 也可能各个状态互相可以转化,例如:与第三方同步数据,同步状态可能从更新待同步直接就变成删除待同步了
    • 定义去重表,先在去重表中插入,成功执行后续操作
    • 悲观锁select for update

对外提供接口为了支持幂等调用,接口有两个字段必须传:

  • 来源source字段
  • 来源方序列号seq字段

这个两个字段在己方系统里面作为唯一标识符,后续在己方系统中使用以上的方法来保证幂等。