1. 引言
- 接口在高并发和复杂业务场景下容易出现的问题:
- 重复请求、重复扣款、雪崩流量
- 用户操作频繁导致的无效请求
- 文章目标:从幂等、防重复提交到限流、防抖节流,令牌桶全方位理解接口安全与性能优化
2. 接口幂等性(Idempotency)
- 定义:同一个业务请求执行一次和执行多次,结果必须一致
- 实现方式:
- 数据库唯一约束(保证重复请求无法产生新数据)
- Redis Set / SETNX(缓存请求标识,防止重复执行)
- 分布式锁(保证同一资源同一时间只有一个请求处理)
- 状态机(订单状态流转控制)
- MQ 异步去重消费(保证消息消费幂等)
- 典型场景:支付回调、下单接口
- 特点:属于事后保障,保证结果一致
3. 防重复提交
- 定义:在短时间内阻止用户重复点击/提交同一个操作
- 实现方式:
- 特点:
- 属于事前拦截
- 关注短时间的重复触发,多见于前端/网关层
- 示例:快速点击支付按钮或提交表单
- 区别于幂等性:防重复提交是减少请求压力,幂等性是保证结果正确
4. 高频事件控制:防抖(Debounce)与节流(Throttle)
- 防抖(Debounce):
- 在指定时间窗口内,多次触发的事件只执行最后一次,忽略之前重复触发
- 适用场景:输入框搜索+文本编辑器实时保存
- 注意:如果一直持续触发,计时器一直被重置,动作永远不会执行,所以防抖不适合必须立即响应的操作
- 节流(Throttle):
- 在指定时间窗口内,限制接口的调用频率,只允许固定次数的执行
- 适用场景:滚动加载、窗口 resize、按钮限频
5. 接口限流与控制策略
- 令牌桶(Token Bucket):
- 控制接口调用频率,允许短时间突发流量
- 原理:固定速率生成令牌,请求消耗令牌执行
- 分布式锁:
- 确保同一资源同一时间只处理一个请求
- 多用于分布式幂等操作
6. Redis 实现
- Set 集合:
- SETNX / SET NX EX:
- 原子操作保证同一请求只处理一次
- 可设置过期时间,避免锁死
- 应用场景:短期幂等控制、防重复提交
7. MQ 异步去重消费
① 常见 MQ 去重方式
- RocketMQ(≥5.x):自带消息去重功能(基于唯一 keys + Broker 内存缓存)
- Kafka:支持幂等生产(
enable.idempotence=true)避免重复写入
- RabbitMQ:原生不去重,可用社区 deduplication 插件(基于
message_id 或 header)
- 通用方案:消费端用 Redis / 数据库 去重 + 业务幂等兜底
② 基本原理
- 每条消息附带业务唯一 ID(如订单号、支付流水号)。
- 消费前先检查缓存/数据库是否已处理过该 ID。
- 如果已处理 → 丢弃;否则 → 执行消费逻辑并记录已处理标记。
③ 优点
- 防止重复消费导致的重复扣款、重复扣库存、重复通知等问题
- 提高异步消费的幂等性和数据一致性
④ 典型场景
- 支付回调(防止重复扣款)
- 订单异步处理(防止重复创建订单记录)
- 第三方异步通知(防止重复发短信/邮件)
8. 总结
- 防重复提交:事前拦截,减少短期重复请求压力
- 幂等性:事后保障,保证业务结果正确
- 防抖 / 节流 / 令牌桶:高频操作和接口访问频率控制
- Redis / 分布式锁 / MQ:结合使用,形成完整接口安全与性能优化方案
我用 12306 购票 这个大家都熟悉的场景,把
接口幂等性、防重复提交、令牌桶、防抖、节流 全部串起来讲一遍。
🚄 12306 购票场景全解
1️⃣ 防抖(Debounce) — 防止“输入还没结束就去查票”
场景:你在 12306 搜索框输入“北京南 → 上海虹桥”,每敲一个字都发一次查询接口会非常浪费资源。
做法:
- 设置防抖 500ms:只有你停止输入 500ms 之后,才发一次查询请求。
- 如果你一直在改输入(比如“北…北京…北京南”),一直不会发请求。
目的:避免无效频繁请求,减少服务器压力。
2️⃣ 节流(Throttle) — 控制“点查询的频率”
场景:你疯狂点击“查询余票”按钮,可能会对服务器造成很大压力。
做法:
- 设置节流:比如 2 秒内最多只能发一次查询请求。
- 你可以一直点,但 2 秒内只会真正发一次请求,其余的会被丢弃或延迟到下一个时间窗口。
目的:控制频率,平衡用户体验与系统性能。
3️⃣ 令牌桶(Token Bucket) — 控制总并发量
场景:春节抢票时,全国几百万人同时点“立即抢票”,如果不限制,服务器会直接崩。
做法:
- 服务器有一个“令牌桶”,桶里有令牌才允许访问接口。
- 每秒生成一定数量的令牌(比如 1000 张),先到先得,没有令牌的请求直接拒绝或排队。
目的:防止高并发冲垮系统,实现平滑限流。
4️⃣ 防重复提交 — 防止短时间重复操作
场景:你点“立即支付”时,网络卡顿,你不耐烦又点了好几次。
做法:
- 前端:按钮第一次点击后立刻禁用。
- 后端:用 Redis 记录
用户ID+订单ID,5 秒内拒绝重复请求。
目的:防止用户短时间重复触发同一操作,减少接口压力。
5️⃣ 接口幂等性 — 确保业务结果一致
场景:你支付过程中网络闪断,刷新页面后再次提交支付请求。
做法:
- 每个订单有唯一
orderNo,后端判断订单状态,如果已支付,不会再次扣款。
目的:保证无论用户提交多少次,请求结果都一样,不会重复出票或扣费。
📌 12306 全流程整合
- 搜索余票
- 输入框防抖,防止边打字边查
- 点击查询节流,防止频繁点击
- 抢票接口
- 确认订单
- 支付接口
🎯 总结记忆
💡 一句话记:
- 防抖:等你说完再干活
- 节流:你可以喊很多次,但我按频率回应
- 令牌桶:进门要票,没有就等
- 防重复提交:你短时间按几次我只认第一次
- 幂等性:不管你按多少次,结果都一样