用户登录

技术文章

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

设计稳定的 API 限流与计费系统:先统一计数口径,再选择失败策略

一句话回答

限流与计费应拆成两条链路:二者共享计数口径,但限流追求低延迟拒绝,计费追求可追溯对账。设计时先固定计数单位与时间窗;限流采用本地快速判定加全局配额校准;计费采用带幂等键的预扣—结算流程;并为限流器或计费存储不可用的情况显式选择 fail-open 或 fail-closed。具体产品的配额与计费参数待核实。

核心要点 TL;DR
  • 先定义计数口径(单位、时间窗、维度),再选限流算法;口径不一致时,算法再精确也难对账。
  • 限流可在请求路径做本地快速判定,再用全局配额周期性校准,代价是接受有限的跨实例计数漂移。
  • 计费用预扣—结算加幂等键,账本只追加;失败路径要显式选择放行或拒绝,并给补偿设上限。

要让限流和计费同时稳定,关键不在于选一个更复杂的算法,而在于先把两件事拆开:限流决定“此刻能不能放行”,计费决定“事后记多少账”。前者要求低延迟和明确的拒绝语义,后者要求可追溯、可对账。二者必须共享同一套计数口径,但对一致性的要求并不相同。本文给出通用设计要点;API.PLUS 的具体配额单位、限流响应头、计费粒度与结算周期,本次材料未提供,需以官方文档为准。

一、先固定计数口径,再讨论算法#

口径不清是限流与计费对不上的最常见原因。落地前至少回答四个问题:

  • 计什么:请求次数、token 数、并发连接数,还是传输字节?多维度计费时,各维度的换算关系要写死。
  • 时间窗:固定窗口还是滑动窗口?跨窗口边界的请求算在哪一侧?固定窗口实现简单,但在窗口切换处可能出现双倍突发。
  • 维度:按 API 密钥、账号、IP 还是组合维度计数?维度越多,存储与基数成本越高。
  • 单位与精度:金额和配额建议用整数最小单位累加,避免浮点误差在长期账本里累积。

建议把口径整理成一张表,限流模块和计费模块引用同一份定义,而不是各自实现一遍。

二、限流放在请求路径上,分两段做#

常见做法是本地快速判定加全局配额校准:每个实例维护本地令牌桶,请求到达时先本地判断,避免每次请求都访问中心存储;同时周期性从全局计数器领取一段配额,领完再取。这样延迟低,代价是跨实例存在短暂的计数漂移,需要用突发上限和领取粒度把误差控制在可接受范围内。

如果业务无法接受漂移,就得把判定收敛到中心存储,用原子操作保证一致性,但要接受额外的网络往返和中心存储的可用性依赖。这是一个明确的取舍,不存在两边都占的方案。

三、计费走预扣与结算,幂等键不能省#

按用量计费的系统建议采用 reserve → 执行 → commit/rollback 三段式:请求开始时预扣额度,成功后结算实际用量,失败则释放。执行过程中若进程崩溃,预扣会悬挂,因此需要超时回收机制。

重试和回调重复是常态,写入账本必须带幂等键(通常是请求 ID 或调用方生成的唯一键)。账本采用只追加结构,修正通过冲正记录完成,而不是原地改数。

# 未执行示例:伪代码,语言无关,仅示意预扣与幂等流程
def reserve(request_id, units):
    key = "quota:" + request_id
    if ledger.exists(key):          # 重复请求直接返回旧结果
        return ledger.get(key)
    if balance.available() < units:
        raise QuotaExceeded
    hold = balance.reserve(units)   # 预扣
    ledger.append(key, "reserve", units)
    return hold

def settle(request_id, actual_units):
    hold = ledger.get("quota:" + request_id)
    ledger.append("quota:" + request_id, "commit", actual_units)
    balance.release(hold.units - actual_units)

以上仅为流程示意,未在真实环境执行;实际实现还需处理并发写入、账本分区与超时回收。

四、失败路径要显式选择#

限流器不可用时,是放行(fail-open,承担超额风险)还是拒绝(fail-closed,影响可用性),必须提前写进设计文档,而不是等故障时临时决定。计费存储不可用时,可以先写本地日志再异步补偿,但要给丢失窗口设一个上限,并明确超过上限后的处理方式。两种选择都会产生可观测的后果,关键是让后果可度量。

五、对账与可观测性#

至少保留三个可比的数:放行数、记账数、拒绝数。差异要能下钻到具体请求 ID,否则对账只能停留在总量层面。建议观测的指标包括拒绝率、剩余配额分布、预扣未结算的时长,以及补偿队列积压量。

边界说明:以上为通用工程设计建议,不构成对任何具体服务实际行为的描述,也没有经过实测验证。涉及 API.PLUS 的真实配额参数、限流响应语义和计费规则,本次材料未提供,待核实官方文档后再补具体实现细节。

作者:沐小悠

本文约 5 分钟读完 · 关键词:API 限流、计费系统、令牌桶、滑动窗口、预扣结算

更多文章 GET 示例接口

「」标签下的其他文章