用户登录

技术文章

外部链接将在新标签页打开。

限流与计费别各写一套:一次 API 调用的身份、预占与账本设计

一句话回答

限流和计费若各自计数,出故障时无法区分“被限流”和“记错账”。更稳的做法是共用一条决策链:统一请求身份,限流计数与计费账本分开存储,按“预占—执行—结算—超时回收”推进,账本用请求 ID 做主键实现幂等,并保留悬挂单与差额等对账指标。单实例小流量不必引入这套结构。

核心要点 TL;DR
  • 限流计数允许少量误差,计费计数不可丢、不可重;两者共用身份,但不共用存储。
  • 预占、结算、超时回收三段式,把“余额不足”和“业务失败”分开处理。
  • 账本主键用请求 ID,重复写入自然被拒,幂等成本最低。

核心判断:限流和计费如果各写一套计数,出问题时你无法区分“这次请求被限了”还是“账记错了”。更稳的做法是让两者共用一条决策链——先确定这次调用的身份,再决定是否放行,最后把结果写成一条可重放的账本记录。

先分清三类数据#

  • 身份:租户、密钥、端点、来源 IP。维度越多,存储与聚合成本越高。建议主维度定为“租户 + 密钥 ID”,端点和 IP 只作为辅助阈值。
  • 限流计数:允许少量误差,随窗口重置,进程重启后丢失通常可接受。
  • 计费计数:不可丢、不可重,需要幂等键和持久账本。

把限流计数当账本用,或把账本当限流计数用,是两类常见的设计错误:前者会导致账单抖动,后者会让每次计费查询都压在限流的关键路径上。

一次请求的决策顺序#

  1. 认证,得到租户 ID 与密钥 ID。不要用明文密钥本身做计数键。
  2. 幂等检查:同一请求 ID 已有账本记录,直接返回上次结果。
  3. 配额预占:按本次调用可能的最大消耗预占额度。
  4. 执行业务逻辑。
  5. 结算:把预占改为实际用量,多退少补。
  6. 返回响应,落用量记录。

预占与结算分开,是为了不把“余额不足”和“业务失败”混为一谈。业务失败要不要计费是产品规则,需要在接入文档中明示,不能由实现临时决定。

计数放在哪里#

单机内存计数最快,但重启即清零、多实例不共享,只适合做粗粒度的自我保护。需要跨实例一致时,把计数放到集中的键值存储,并用脚本或事务把“读—比较—写”合成一个原子操作;具体原子命令与过期语义随版本不同,落地前需按所用版本的官方文档核实。

一个常见折中是:限流用集中计数加短窗口,账本用数据库。两者的降级方向不应相同——限流组件不可用时,可以按“先放过、记入待核对”处理;账本写入失败时,应当拒绝放行,否则就是无账可查的免费调用。

一个最小的账本结构#

以下为示意结构,未执行,字段按实际业务增删:

CREATE TABLE usage_ledger (
  request_id  VARCHAR(64) PRIMARY KEY,  -- 幂等键,由调用方或网关生成
  tenant_id   VARCHAR(64) NOT NULL,
  key_id      VARCHAR(64) NOT NULL,
  endpoint    VARCHAR(128) NOT NULL,
  reserved    BIGINT NOT NULL,          -- 预占额度
  settled     BIGINT,                   -- 实际用量,NULL 表示尚未结算
  status      VARCHAR(16) NOT NULL,     -- reserved / settled / released
  created_at  TIMESTAMP NOT NULL
);

主键放在 request_id 上,重复写入会被唯一约束直接拒绝,这是幂等最省事的实现。把 reserved 与 settled 分开存,对账时能直接看出“预占了但没结算”的悬挂单,不必反推。

结算失败与超时回收#

悬挂单需要回收机制:超过约定时间仍是 reserved 的记录,要么按预占额扣费,要么整笔释放。两种策略对用户的影响不同,属于业务决策,应在文档中写明。回收任务本身也必须幂等,重复执行不能产生第二次扣费。

对账与观测#

  • 按租户统计预占与结算的差额,差额持续为正说明结算链路有漏。
  • 悬挂单数量与账龄分布。
  • 按端点的限流拒绝率。
  • 账本写入失败数与重试次数。

拒绝率突然升高,可能是阈值定得太紧,也可能是计数键选错导致所有租户共用同一个桶——后者应在维度设计阶段就排除,而不是等报警后再查。

什么时候不需要这套结构#

单实例、对内、调用量小的服务,一个内存计数器加一行日志就够了。预占、结算、对账的价值随并发量和计费维度上升,过早引入只会增加故障点,还要额外维护回收任务。

API.PLUS 侧的具体配额粒度、计费单位与失败计费规则,本文未取得可核验的官方说明,不作确认,请以其官方文档为准(待核实)。文中表结构仅用于说明设计关系,不代表任何平台的真实实现。

常见问题(FAQ)

业务失败要不要计费?
这属于产品规则,不是实现细节,需要在接入文档中写死。技术上把预占与结算分开,规则调整时不必改计数链路。
限流和账本能不能共用一个存储?
可以但不建议。限流是短窗口高频读写,账本需要持久与幂等;两者对失败和丢失的容忍度不同,降级方向也应不同。

作者:沐小悠

本文约 5 分钟读完 · 关键词:API 限流、计费系统、配额预占、幂等账本、请求身份

更多文章 GET 示例接口

「」标签下的其他文章