分布式限流预留与退款:让并发请求共享一份容量账
请求进入时只能估算成本,完成后才知道真实消耗;若多个网关各自扣减、超时就退款,容量会被重复花费。本文用预留 receipt、结算与过期竞争维护限流守恒。
本文要点
- 主题聚焦:TypeScript、DevOps
- 适合人群:独立开发者、全栈工程师、AI 应用开发者
- 核心价值:快速理解背景、实践路径、常见误区和工程建议
三个网关都看到租户还剩 1000 token,于是同时放行三个预计消耗 600 的请求;执行器最终只用了 200,客户端又在超时后重试。只在入口做一次 remaining > 0 检查会超卖,只在成功后扣费会让并发没有上限,而一超时就全额退回又可能让仍在运行的请求免费占用容量。
API 限流策略介绍固定窗口、滑动窗口和令牌桶,LLM 限流与配额讨论常见维度。本篇不再比较算法,而是处理估算成本、实际消耗和取消之间的状态:一次准入如何先预留,之后只结算或退回一次。
把策略代际与预留身份冻结
发布 admissionBudget.v1,固定 subject key、route、region、容量单位、补充规则、burst、估算器 digest、最大单次预留、hold TTL、优先级、应急模式和 policy generation。RFC 2697 描述 token bucket meter 的参数,但跨网关的业务预留仍需线性化账本;RFC 6585 的 429 只表达请求过多,可携带 Retry-After,并不证明内部余额如何计算。
limitReservation.v1 保存 reservation ID、规范请求摘要、主体、策略 generation、accounting epoch、reserved units、actual units、operation ID、expiresAt 和状态。相同幂等键但请求摘要不同必须拒绝。状态为 REQUESTED -> RESERVED -> STARTED -> SETTLED,未开始可到 RELEASED,到期竞争进入 EXPIRING -> EXPIRED;扣减响应丢失时为 RESERVATION_UNKNOWN,执行结果未知时为 USAGE_UNKNOWN。
schema: limitReservation.v1
reservation: rl-01J...
subject: tenant-7/chat.generate
policyGeneration: 31
accountingEpoch: 2026-07-19T14:00Z
requestDigest: sha256:71a...
reservedUnits: 600
state: RESERVED
只有 ledger 可以花掉容量
唯一准入点 reserveCapacity 在线性化事务中锁定 (subject, route, accountingEpoch, policyGeneration),写 reservation receipt 后才减少 available。重复请求返回同一 receipt。连接断开时按原 reservation ID 查询,不能换 ID 再扣。离线 emergency 额度只能在健康期由中央账原子从 available 移入 retiredForEmergency,签发不重叠的 emergencyShard.v1;分区发生后不得临时复制中央余额。
执行器开始前以 CAS 将 RESERVED 改为 STARTED 并绑定 operation ID;到期任务只能把仍为 RESERVED 的项目转为 EXPIRED。完成 receipt 给出 actual units,settleUsage 原子写 SETTLED 并退回未用差额。若 actual 超过预留,先消费 available;不足部分全部记入 overdraftIssued 和 spent,状态为 SETTLED_WITH_DEBT,立即暂停该 subject/route 新准入。策略规定最大 overdraft 与执行硬上限;越界仍记全额 breach debt 并冻结 route,不能从守恒式删除。
中央守恒为 opening + minted + overdraftIssued = available + reserved + spent + retiredForEmergency + overdraftRepaid,债务是 issued - repaid;后续 refill 先写 overdraftRepaid,债清零才恢复准入。每个 emergency shard 另满足 allocated = localAvailable + localReserved + localSpent + localRetired,并绑定 gateway identity、boot epoch、fence、有效期和签名。重启不能继承旧 shard;回连只凭终态 receipt 把确认未用额度在新 generation 重新 mint,已用和 UNKNOWN 仍保持 retired。AI 平台租户配额负责多维上限,预付 Credits 预留结算是财务价值账,不能共用事实源。
公平性在容量守恒之后处理。调度器可按租户权重和优先级挑选已 RESERVED 请求,但不能绕开 ledger。拒绝响应携带命中的策略 generation、限制维度和保守 Retry-After,不泄露其他租户余额。调用超时与未知效果的恢复遵循跨服务 Deadline:取消传播不等于执行已经停止,只有 operation receipt 能结束 USAGE_UNKNOWN。
用重复、超时和切代攻击守恒
故障注入覆盖两个网关同时预留、STARTED 与 EXPIRED 竞争、actual 超预留、debt repayment ACK 丢失、中央账本分区、两个 gateway 复用 shard、进程重启后重放旧 shard、未用额度重复回收、退款 ACK 丢失和 generation 交叉。断言中央与每个 shard 守恒,available 永不为负,未清 debt 时准入为 0,重复退款/回收为 0,UNKNOWN 不被当成未消费。
发布门要求一百万次交错中中央/分片守恒差异、超发、第二 reservation、重复 shard 消费和漏记 debt 均为 0,UNKNOWN p99 在 10 分钟内收敛,且各租户误差不越公平预算。上线先 shadow,再让 1% route 真预留;emergency shard 先保持禁用。回滚停止新 generation 准入,但保留 settlement、debt 和 shard 对账 worker 直到全部归零;旧限流器只能消费自己的预分配预算,绝不能把 retired 额度整包退回。
Related