如何保证接口幂等性?一口气说了11种方法!

1,274 阅读7分钟

今天咱们来聊聊接口幂等性。在后端开发中,接口幂等性是一个非常重要的概念,尤其是在处理支付、订单等关键业务时。如果接口幂等性没有处理好,可能会导致严重的后果,比如重复支付、重复下单等。今天,我就来给大家详细讲讲什么是接口幂等性,以及如何保证接口幂等性。


一、什么是接口幂等性?

幂等性原本是数学上的概念,用在接口上可以理解为:同一个接口,多次发出同一个请求,必须保证操作只执行一次。换句话说,无论你调用多少次接口,最终的结果都是一样的,不会因为重复调用而产生副作用。

举个例子,支付接口如果幂等性没有处理好,用户可能会因为网络波动或误操作而重复支付,导致账户被扣款多次。再比如订单接口,用户可能会因为页面刷新或重复点击而多次提交订单,导致系统生成多个重复订单。


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

接口幂等性问题通常是由以下几种情况引起的:

  1. 网络波动:网络不稳定可能导致请求被重复发送。
  2. 用户重复操作:用户可能会因为操作延迟或误操作而多次点击按钮。
  3. 重试机制:应用层、RPC 或 Nginx 的重试机制可能导致请求被重复处理。
  4. 页面重复刷新:用户可能会因为操作延迟而刷新页面,导致表单重复提交。
  5. 浏览器后退或前进按钮:用户可能会通过浏览器的后退或前进按钮重复提交表单。
  6. 定时任务重复执行:定时任务可能会因为配置错误或异常而重复执行。
  7. 双击提交按钮:用户可能会因为操作习惯而多次点击提交按钮。

三、如何保证接口幂等性?

保证接口幂等性的方法可以分为两大类:客户端控制服务端控制。客户端控制相对简单,但不够可靠;服务端控制则更加复杂,但更有效。下面,我来详细介绍一下这些方法。

(一)客户端控制

1. 按钮只可操作一次

最简单的方法是将按钮设置为只可操作一次。用户点击提交按钮后,立即将按钮置为不可用状态或显示加载动画,防止用户重复点击。这种方法简单易行,但只能解决用户误操作的问题,无法应对网络波动或重试机制导致的重复请求。

2. Token 机制

Token 机制是一种常见的解决方案。用户在进入页面时,后端生成一个唯一的 Token 并存储在 Session 或数据库中,同时将 Token 返回给前端。前端在每次请求时将 Token 作为参数发送给后端,后端在处理请求前验证 Token 是否有效。如果 Token 有效,则处理请求并将其标记为已使用;如果 Token 无效,则返回错误提示。这种方法可以有效防止重复提交,但需要后端和前端配合实现。

3. 使用 Post/Redirect/Get 模式

Post/Redirect/Get(PRG)模式是一种常见的解决方案。用户提交表单后,后端处理请求并返回一个重定向响应,将用户引导到一个信息页面。这样,即使用户刷新页面或使用浏览器的前进/后退按钮,也不会重复提交表单。这种方法可以有效防止重复提交,不过同样需要后端和前端配合实现。

(二)服务端控制

1. 使用唯一索引

利用数据库的唯一索引机制可以有效防止重复数据插入。例如,订单表可以使用订单号作为唯一索引,当重复请求尝试插入相同订单号的数据时,数据库会抛出唯一索引冲突的异常,从而阻止重复数据插入。这种方法简单易行,但只能防止重复插入,无法处理更新操作。

2. 乐观锁

乐观锁是一种常见的并发控制机制。在表结构中添加一个版本号字段(version),每次更新数据时,版本号加一。在处理更新请求时,后端会检查版本号是否一致。如果版本号一致,则更新数据并增加版本号;如果版本号不一致,则返回错误提示。这种方法可以有效防止并发更新导致的重复操作,但需要在表结构中添加额外字段。

3. Select + Insert/Update/Delete

在操作之前先查询数据库,判断是否已经存在相同的数据。如果不存在,则执行插入或更新操作。这种方法在单 JVM 环境中可以有效防止重复操作,但在分布式环境中需要结合分布式锁来保证幂等性。

4. 分布式锁

在分布式系统中,可以使用分布式锁来保证幂等性。例如,使用 Redis 或 Zookeeper 实现分布式锁。在处理请求时,后端会尝试获取分布式锁。如果获取成功,则处理请求并释放锁;如果获取失败,则返回错误提示。这种方法可以有效防止分布式环境下的并发操作,但需要引入第三方系统。

5. 状态机幂等

在设计单据相关的业务时,可以使用状态机来保证幂等性。例如,订单状态可以分为“待支付”、“已支付”、“已发货”等。如果订单已经处于“已支付”状态,再次调用支付接口时,可以直接返回成功响应,而不进行实际操作。这种方法可以有效防止重复操作,但需要对业务逻辑进行详细设计。

6. 防重表

防重表是一种常见的解决方案。后端创建一个防重表,记录每个请求的唯一标识符。在处理请求时,后端会检查防重表中是否存在相同的标识符。如果不存在,则插入记录并处理请求;如果存在,则返回错误提示。这种方法可以有效防止重复请求,但需要额外维护防重表。

7. 缓冲队列

将请求快速接收并放入缓冲队列中,后续使用异步任务处理队列中的数据。在处理过程中,可以过滤掉重复的请求。这种方法可以提高系统的吞吐量,但无法及时返回请求结果,需要后续轮询获取处理结果。

8. 全局唯一号

在请求中使用全局唯一号(如 UUID)作为标识符。后端根据唯一号判断请求是否重复。如果重复,则返回错误提示;如果不重复,则处理请求并记录唯一号。这种方法可以有效防止重复请求,但需要生成和管理全局唯一号。


四、总结

接口幂等性是后端开发中常见的问题,尤其是在处理支付、订单等关键业务时。保证接口幂等性的方法可以分为客户端控制和服务端控制两大类。客户端控制相对简单,但不够可靠;服务端控制则更加复杂,但更有效。具体选择哪种方法,需要根据实际业务场景和需求来决定。

最后分享一份大彬精心整理的大厂面试手册,包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等高频面试题,非常实用,有小伙伴靠着这份手册拿过字节offer~

围观朋友⭕:dabinjava