当前位置: 首页 > news >正文

REST接口幂等设计深度解析

在日常的 Spring Boot 开发中,REST 接口是最常用的对外交互方式。但在实际业务场景下,我们常常会遇到一些隐蔽而又影响体验的问题,比如 幂等性保证、表单重复提交、接口防抖 等。如果处理不当,很容易导致脏数据、并发异常,甚至系统性能问题。

一、接口幂等性问题

1.1 什么是幂等性?

幂等性(Idempotency)是指 同一个接口多次调用,产生的结果与调用一次相同

  • 举例:
    • 查询接口/order/123,调用一次还是多次,返回结果一样 → 天然幂等
    • 新增订单接口/order/create,多次调用可能生成多个订单 → 非幂等

1.2 幂等性常见问题

在业务中,如果没有保证幂等性,可能会出现以下问题:

  • 用户点击支付按钮两次,生成两笔支付请求;
  • 分布式系统中网络重试导致请求重复到达;
  • 消息队列消费重复执行,导致数据库多写数据。

1.3 解决方案

(1)数据库唯一约束

最简单的办法是在数据库层加唯一索引,保证数据不会重复插入。

ALTER TABLE orders ADD UNIQUE KEY (order_no);

在 Spring Boot 中,写入时如果捕获 DuplicateKeyException,可以认为是重复请求。

(2)幂等 Token 机制

用户在请求敏感接口前,先向服务端申请一个 唯一 Token,请求时必须带上 Token,且只能使用一次。
流程如下:

  1. 客户端请求 POST /token 获取 Token;
  2. 客户端请求业务接口时携带该 Token;
  3. 服务端验证 Token 是否存在并删除,保证只用一次。

Spring Boot 示例(Redis 存储 Token):

@PostMapping("/token")  
public String getToken() {  String token = UUID.randomUUID().toString();  redisTemplate.opsForValue().set(token, "1", 5, TimeUnit.MINUTES);  return token;  
}  @PostMapping("/order/create")  
public ResponseEntity<String> createOrder(@RequestHeader("Idempotency-Token") String token) {  Boolean exists = redisTemplate.delete(token);  if (exists == null || !exists) {  return ResponseEntity.badRequest().body("重复请求或 Token 失效");  }  // 执行业务逻辑  return ResponseEntity.ok("订单创建成功");  
}
(3)业务幂等 Key

对于某些业务,可以直接使用 业务 ID 作为唯一幂等 Key,比如订单号、用户操作流水号。

二、重复提交问题

2.1 问题场景

用户在操作页面时,经常出现以下问题:

  • 网络卡顿,用户多次点击提交按钮;
  • 表单重复提交,导致多次写入数据库;
  • 页面刷新(F5),导致表单重复提交。

2.2 常见解决方案

(1)前端按钮防重复

最简单的方式:按钮点击一次后立即置灰/禁用,避免用户手动多次点击。
但缺点是只能避免大部分情况,无法彻底防止恶意请求

(2)后端请求防重复

在后端增加拦截器,基于 用户 ID + 请求 URL + 参数 生成请求签名,并在短时间内拒绝相同请求。
Spring Boot 拦截器示例:

@Component  
public class RepeatSubmitInterceptor implements HandlerInterceptor {  private final Map<String, Long> requestCache = new ConcurrentHashMap<>();  @Override  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {  String key = request.getSession().getId() + ":" + request.getRequestURI() + ":" + request.getQueryString();  long now = System.currentTimeMillis();  Long lastTime = requestCache.get(key);  if (lastTime != null && now - lastTime < 3000) {  throw new RuntimeException("请勿重复提交");  }  requestCache.put(key, now);  return true;  }  
}

该方式能有效拦截 3 秒内的重复请求

(3)Token + Session 验证

结合幂等性 Token,在提交表单时校验 Session 中的 Token 是否已经使用过,避免刷新导致重复提交。


三、防抖与限流策略

3.1 防抖(Debounce)

防抖是指:多次触发同一操作,只在最后一次触发后 N 毫秒执行。
典型场景:

  • 搜索框输入建议,避免每次输入都触发接口;
  • 高频点击按钮,避免连续调用后端 API。

前端可用 JavaScript 实现:

function debounce(fn, delay) {  let timer;  return function(...args) {  clearTimeout(timer);  timer = setTimeout(() => fn.apply(this, args), delay);  }  
}  document.getElementById("search").addEventListener("input", debounce(() => {  console.log("触发搜索请求");  
}, 500));

3.2 限流(Rate Limiting)

对于接口高并发请求,需要在后端限流。 Spring Boot 中常见方案:

  • Guava RateLimiter:基于令牌桶;
  • Redis + Lua 脚本:分布式限流;
  • Spring Cloud Gateway:在网关层统一限流。

示例(Guava RateLimiter):

@RestController  
public class RateLimitController {  private final RateLimiter rateLimiter = RateLimiter.create(5.0); // 每秒 5 个请求  @GetMapping("/api/data")  public String getData() {  if (!rateLimiter.tryAcquire()) {  return "请求过多,请稍后再试";  }  return "成功返回数据";  }  
}

四、总结

在 Spring Boot 开发 REST 接口时,常见的三个问题是:

  1. 幂等性:数据库唯一约束、幂等 Token、业务 Key;
  2. 重复提交:前端按钮禁用、后端拦截器、Token 验证;
  3. 防抖与限流:前端防抖,后端限流(RateLimiter/Redis/Gateway)。

这三类问题看似细节,但如果不加以控制,会导致 数据重复、性能下降、用户体验变差
在实际项目中,建议:

  • 查询接口默认幂等
  • 写操作接口必须保证幂等性
  • 对高频接口添加限流和防抖
  • 结合业务特点选择适合的策略

文章转载自:

http://AqJ2DnKG.znhpg.cn
http://98irNfu5.znhpg.cn
http://Rw7txc9w.znhpg.cn
http://uCJs1QOe.znhpg.cn
http://q59Djx3g.znhpg.cn
http://p2zJW84B.znhpg.cn
http://iCAVJKX7.znhpg.cn
http://j0f5vufr.znhpg.cn
http://w3LjDTSf.znhpg.cn
http://k6ATTCUy.znhpg.cn
http://RjTWSTyP.znhpg.cn
http://MafCTC0t.znhpg.cn
http://3NbpfZvQ.znhpg.cn
http://0J8Pv5eP.znhpg.cn
http://KO9eUxqx.znhpg.cn
http://SR3yIka8.znhpg.cn
http://dTKWbkaD.znhpg.cn
http://bpnAKdSj.znhpg.cn
http://QyF6Vvpr.znhpg.cn
http://Osd6N84A.znhpg.cn
http://AxPMzB2q.znhpg.cn
http://E63JSOjR.znhpg.cn
http://oo6Mot9M.znhpg.cn
http://AoV2ZDZv.znhpg.cn
http://ClpcZXPV.znhpg.cn
http://MQui39Ik.znhpg.cn
http://ULPN5aPm.znhpg.cn
http://f9hzW40D.znhpg.cn
http://kjKGCOYw.znhpg.cn
http://qCKbsRWZ.znhpg.cn
http://www.dtcms.com/a/374929.html

相关文章:

  • 在Word和WPS文字中便捷切换英文段落大小写
  • 【华为OD】寻找连续区间
  • 渗透测试信息收集步骤与工具详解
  • #C语言——刷题攻略:牛客编程入门训练(十):攻克 循环控制(二),轻松拿捏!
  • 乐吾乐大屏可视化组态软件【SQL数据源】
  • 打工人日报#20250909
  • PyTorch中的flatten操作详解:从start_dim=1说起
  • 上网行为审计软件应该如何选择?适配图书馆管理的上网行为审计软件推荐
  • 计算机网络第五章(1)——传输层(概念 + UDP)
  • AI 时代,我们是否应该重温极限编程?
  • Protobuf 新版“调试表示为什么有链接?为什么会打码?我该怎么改代码?
  • php 使用html 生成pdf word wkhtmltopdf 系列1
  • vcsa6.0 升级6.7
  • python中的深拷贝与浅拷贝详细介绍
  • 【Java】Hibernate二级缓存下
  • R 包的管理涉及两个概念:二进制包的下载缓存位置和包的最终安装位置。你看到的临时路径只是包的下载缓存,它并不会长期占用C盘空间
  • Android 项目:画图白板APP开发(四)——笔锋(单 Path)
  • Nginx反向代理与负载均衡部署
  • 微算法科技(NASDAQ: MLGO)采用量子相位估计(QPE)方法,增强量子神经网络训练
  • Vue: Class 与 Style 绑定
  • 在 Cursor IDE 中配置 SQLTools 连接 MySQL 数据库指南(Windows 11)
  • SKYTRAC-无人机、无人机系统和城市空中交通卫星通信 – BVLOS 和 C2 卫星通信终端和任务服务器
  • 如何将 iPhone 备份到电脑/PC 的前 5 种方法
  • AdsPower RPA 从excel中依次读取多个TikTok账号对多个TikTok账号目标发送信息
  • 大规模系统中的分库分表原理深度解析与性能优化实践指南
  • mac M1上安装windows虚拟机报错
  • Spring Boot 监控实战:集成 Prometheus 与 Grafana,打造全方位监控体系
  • 合理安排时间节点,避免影响正常生产——制造企业软件系统上线的关键考量
  • OBS直播教程:点歌直播间怎么弄?直播点歌用什么软件?
  • Apache Cloudberry 2.0.0 发布:1981 项变更提交,续写 Greenplum 未竟之路