接口幂等性

一、什么是接口幂等性?

幂等性 来源于数学概念,指一个操作执行一次与执行多次,所产生的系统副作用(即对资源状态的影响)是完全相同的。

接口幂等性 则特指:客户端用相同的参数,对同一个接口发起一次或多次调用,其最终的业务结果(或系统资源状态)应该是一致的。

简单来说:

· 非幂等接口:“我重复扣了你两次钱,但货只发了一件。”

· 幂等接口:“无论你因为网络超时、页面刷新等原因重复提交了多少次请求,你的钱只会被扣一次,货也只会发一件。”

二、为什么需要幂等性?

在分布式、微服务架构以及不稳定的网络环境下,重复请求是不可避免的:

1. 前端/客户端问题:用户手速快,多次点击提交按钮;页面刷新后自动重发请求。

2. 网络问题:请求成功到达服务器并处理后,返回响应时网络超时或中断,客户端认为失败并自动重试(如 RPC/HTTP 重试机制)。

3. 微服务间调用:服务A调用服务B,B处理成功但返回响应时超时,A触发熔断或重试逻辑,再次调用B。

如果接口不保证幂等性,就会导致:

· 重复扣款

· 重复创建订单

· 重复发放优惠券

· 数据库中出现重复数据

三、核心实现方案

实现幂等性的核心思想是:让服务端能够识别并过滤掉重复的请求。以下是几种主流方案,从简单到复杂:

方案一:Token 机制(适用于前后端交互)

这是最常用、最直观的方案,尤其适用于防止表单重复提交。

1. 生成令牌:客户端(如网页)在请求业务接口前,先请求一个“唯一令牌”(Token)。服务端生成一个全局唯一的 Token(如 UUID),并将其存储在 Redis 或内存中(设置较短的有效期),然后返回给客户端。

2. 携带令牌:客户端在发起真正的业务请求时(如提交订单),必须将此 Token 作为参数(通常放在请求头,如 Idempotent-Token: xxxx)一起提交。

3. 验证令牌:服务端收到请求后:

  · 检查 Token 是否存在。

  · 如果存在,执行业务逻辑,然后立即删除这个 Token。

  · 如果不存在,说明这个 Token 已被使用过(请求是重复的),直接拒绝请求或返回之前的处理结果。

4. 优点:简单,逻辑清晰。

5. 缺点:需要一次额外的“获取令牌”请求;在高并发下,需保证“检查并删除 Token”操作的原子性(Redis 的 SETNX 或 Lua 脚本)。

方案二:唯一索引(适用于数据库层防重)

利用数据库的唯一键约束,防止产生重复数据。

1. 操作:对于需要防重的业务,设计一个“业务唯一标识”(如:订单号、流水号、防重ID),并在数据库表中为该字段建立唯一索引。

2. 流程:当请求到来时,尝试将数据(包含这个唯一标识)插入数据库。

  · 如果插入成功,说明是第一次请求,正常处理。

  · 如果因唯一索引冲突导致插入失败,说明是重复请求。此时可以捕获异常,查询已存在的数据并直接返回,不做任何更新操作。

3. 优点:实现简单,可靠性极高,依赖数据库自身能力。

4. 缺点:仅适用于“插入”操作的防重;无法应对“更新”操作(如扣减库存、更新状态)。

方案三:乐观锁(适用于更新操作)

主要用于对数据库记录的更新操作,通过版本号或状态机来保证幂等。

1. 版本号机制:在数据表中增加一个 version 字段。

  ```sql

  UPDATE table_name SET amount = amount - 100, version = version + 1

  WHERE id = 123 AND version = 5;

  ```

  · 首次请求时,version=5,执行成功。

  · 重复请求时,version 已变为 6,条件不匹配,更新影响行数为 0。服务端可以据此判断为重复请求。

2. 状态机机制:定义明确、不可逆的业务状态流转(如:待支付 -> 已支付 -> 已发货)。

  ```sql

  UPDATE order SET status = '已支付' WHERE order_id = 'xxx' AND status = '待支付';

  ```

  · 只有当前状态符合预期时,更新才会成功。重复的支付请求因为状态已不是“待支付”,所以不会产生任何影响。

3. 优点:高效,适用于高频的更新操作。

4. 缺点:需要精心设计业务状态流转。

方案四:去重表(通用方案,功能强大)

可以看作是方案二的升级版和通用化。单独建立一张“请求流水表”或“幂等记录表”。

1. 操作:在执行业务逻辑前,先向“去重表”插入一条记录,其唯一键由 业务场景 + 请求唯一标识 组成(如:pay_20250101120000_abc123)。

2. 流程:

  · 插入成功:继续执行业务逻辑,并在同一个数据库事务中完成业务操作。

  · 插入失败(唯一键冲突):直接返回,不执行业务逻辑。

3. 优点:非常通用,可跨业务、跨服务使用。将幂等性判断与业务逻辑解耦。

4. 缺点:增加了数据库的压力和表数量;同样需要保证“插入去重表”和“执行业务”在同一个事务中,以确保一致性。

四、如何选择方案?

方案 适用场景 优点 缺点

Token 机制 面向用户的写操作,如提交表单、创建订单、支付。 用户体验好,安全直观。 需前后端配合,多一次交互。

唯一索引 简单的数据创建,如生成订单号、流水记录。 实现极其简单,可靠。 只防“插入”,不防“更新”。

乐观锁 带条件的资源更新,如扣减库存、更新状态、账户余额变动。 性能好,与业务结合紧密。 需要设计版本号或状态机。

去重表 分布式、多服务的复杂场景,通用性要求高。 非常灵活和强大,解耦。 实现稍复杂,增加数据库负担。

五、重要注意事项

1. GET 请求是天然幂等的,因为它只用于查询,不产生副作用。幂等性主要讨论 POST、PUT、DELETE、PATCH 等非安全方法。

2. 区分“幂等”和“防重”:

  · 防重:主要防止客户端短时间内无意识的重复提交。

  · 幂等:除了防重,还要保证在任何情况下(如超时重试、消息重投)的多次请求,系统状态都正确。幂等性是比防重更强的要求。

3. 删除操作(DELETE):DELETE /resource/123 执行一次和多次,资源最终都是被删除的,所以是幂等的。

4. 实现粒度:可以是整个接口,也可以是接口中的某一段核心业务逻辑。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容