概述

所谓接口幂等性就是:在特定场景下,同一条件的多次接口调用,保证操作只执行一次,如果接口没有保证幂等性,在以下场景就会产生问题

  • 前端重复提交:用户进行注册、创建个人信息等操作,由于网络抖动导致页面没有及时响应,用户认为没有成功而多次点击提交按钮,发生重复提交表单请求
  • 接口超时重试:提供外部系统调用的接口,因为网络抖动等原因执行成功但没能及时响应,外部系统发起重试,导致重复调用
  • 消息重复消费:使用消息中间件时,消费者手动 ack 确认消息被正常消费时,消费者突然断开连接,已经执行的消息会重新放回队列,被其他消费者重新消费

如何实现接口幂等性?

1. 防重 Token 令牌

具体流程如下:

  • 客户端获取 token,服务端将 token 保存在 redis 中,token 需保证全局唯一
  • 之后客户端发起请求时必须携带 token
  • 服务端校验 token,如果成功则执行业务,并删除 redis 中的 token,否则为重复操作,直接返回结果

这种方式需保证同一请求都携带同一 token,比如同一订单的支付操作都使用同一 token 请求。另外,在并发情况下,Redis 查找数据与删除需要保证原子性,可以使用或 Lua 脚本保证

2. 使用 Redis 实现

这种实现方式是基于 redis 的 setnx 命令实现的,作用是如果 key 不存在,将 key 赋值为 value 并返回 1,若 key 已存在,则不做操作并返回 0

具体流程如下:

  • 客户端请求服务端,将能代表这次请求的唯一标识以 setnx 的方式存入 redis,并根据业务设置相应的超时时间
  • 如果设置成功,代表是第一次请求,执行后续业务逻辑
  • 如果设置失败,代表已经执行过请求,直接返回

redis 是单线程的,所以不会有并发问题

3. 加锁实现

具体流程如下:

  • 客户端请求服务端,对能代表这次请求的唯一标识加锁,保证同一时刻同一请求只有一个能被执行
  • 服务端根据唯一标识判断是否第一次请求,比如查询数据库是否已存在该唯一标识的记录,或者该唯一标识对应记录状态是否为【已完成】
  • 如果是第一次请求,执行后续业务逻辑,否则直接返回

4. 幂等表

加锁会影响性能,我们可以建一张幂等表,为能代表这次请求的唯一标识建立唯一约束

具体流程如下:

  • 客户端请求服务端,服务端会将本次请求的信息插入幂等表
  • 因为有唯一约束,如果幂等表中不存在本次请求记录,则插入成功,执行后续业务逻辑,插入失败,则直接返回

使用这种方式,每次请求都会幂等表新建一条记录,为了避免表数据过大,可以定期进行清理,或者使用流水表来代替幂等表。使用插入而不是查询,也能有效避免并发问题