Blog·Studio
文章系列日历归档关于搜索
Blog·Studio

一个记录思考、笔记与作品的技术博客。

Connect

© 2026 · Blog Studio

鄂ICP备19019526号

crafted with care

stay curious ✦

  1. 文章
  2. ›Agent 多租户隔离与配额工程 2026:从 token 预算到并发熔断的统一运行时

Index

  • 一、问题的提出:多租户 Agent SaaS 的"沉默雪崩"
  • 二、形式化:四层配额 + 三种公平性策略
  • 三、Token 预算的滑动窗口与"双计数器"工程
  • 四、并发槽位的分层令牌桶与熔断
  • 五、GPU 时隙的公平调度(DRR + 优先级衰减)
  • 六、统一视角:四层配额的形式化对偶
  • 七、对工程实践的推论
  • 八、讨论:与已有 Agent 框架的对比
  • 九、给 SRE 的可观测性清单
  • 参考文献

Agent 多租户隔离与配额工程 2026:从 token 预算到并发熔断的统一运行时

把 L1 token 预算 / L2 请求速率 / L3 并发槽位 / L4 GPU 时隙四层配额,和 FCFS / DRR / WDRR 三种公平性策略,与三态熔断器一起,统一到 quota gateway 这个 0.3ms 瓶颈的 stateless 运行时的工程实战。

2026年8月27日·约 22 分钟阅读·6,510 字·11 次阅读·博主
#Agent 技术
Agent 多租户隔离与配额工程 2026:从 token 预算到并发熔断的统一运行时

Index

  • 一、问题的提出:多租户 Agent SaaS 的"沉默雪崩"
  • 二、形式化:四层配额 + 三种公平性策略
  • 三、Token 预算的滑动窗口与"双计数器"工程
  • 四、并发槽位的分层令牌桶与熔断
  • 五、GPU 时隙的公平调度(DRR + 优先级衰减)
  • 六、统一视角:四层配额的形式化对偶
  • 七、对工程实践的推论
  • 八、讨论:与已有 Agent 框架的对比
  • 九、给 SRE 的可观测性清单
  • 参考文献

Agent 多租户隔离与配额工程 2026:从 token 预算到并发熔断的统一运行时

一、问题的提出:多租户 Agent SaaS 的"沉默雪崩"

2026 年,几乎所有严肃的 LLM 应用都不再是"单租户+一次性脚本"的形式,而是以 Agent SaaS 的形态跑在公共推理集群上。一个客户用某个工具型 Agent 跑了 12 小时的无限循环(每 30 秒一轮,每轮消耗 2000 tokens),它并没有触发任何单点错误,但悄悄吃掉了整个集群 17% 的 GPU 时隙;与此同时,另外 30 个付费租户的 P99 延迟从 800ms 一路爬升到 14 秒,客服工单雪崩式涌入,直到凌晨 3 点 SRE 才发现问题。这是典型的"沉默雪崩"——既不是攻击、也不是故障,而是配额缺失带来的次生灾害。本文要解决的就是这个问题:把多租户 Agent 的运行时的四层配额(token / 速率 / 并发 / GPU)、三种公平性策略、熔断器状态机、以及它们在生产环境中的工程实现细节,统一到同一个形式化框架下,并给出可以直接落到代码里的 7 条 SRE 实践。

我们区分"限流(rate limiting)"和"配额(quota)"。限流是对窗口内请求数的硬约束(例如每秒最多 100 个),配额是对资源使用总量的语义约束(例如月度 100 万 tokens)。在多租户 Agent 场景下,这两者必须分层共存:限流管瞬时突发,配额管长尾累积。一个只有限流没有配额的系统,会在"小流量长任务"模式下被击穿——这正是 12 小时循环吃掉 17% GPU 时隙的根因。本文要讨论的所有机制,都默认这一区分作为前提——把限流当作短时"刹车",把配额当作长时"油表",两者协同才能让集群既不被突发压垮,也不被长任务拖垮。

二、形式化:四层配额 + 三种公平性策略

我们把一个多租户 Agent 集群的资源约束抽象为四层:

  • L1 Token 预算:租户在滚动窗口(如 60s / 1h / 30d)内允许消耗的输入+输出 token 总数。
  • L2 请求速率:租户在固定窗口内的请求数 RPS。
  • L3 并发槽位:租户同时处于 in-flight 状态的请求数上限。
  • L4 GPU 时隙:租户在共享 GPU 池中预留的连续推理时隙(以秒或 token-batch 计)。

这四层不是并列关系,而是一个漏斗形叠加——L4 是物理下界,L3 在 L4 之上,L1 在 L3 之上。任何一个租户的请求,如果在任一层被拒绝,请求就会以不同的 HTTP 状态码(429 / 503 / 402)返回,但所有层共享同一个配额账本(我们会在 §3 看到)。

公平性策略有三:

  1. FCFS (First-Come First-Served):按请求到达顺序排队,实现最简单,但小租户容易被大租户"插队"。
  2. DRR (Deficit Round Robin):每个租户维护一个"信用额度 deficit",每轮给一个量子(quantum)的服务,服务用完后扣减;落后租户在下一轮补足。适合 L3 并发槽位。
  3. WDRR (Weighted DRR):在 DRR 基础上给租户加权重(按付费等级 / 月度配额份额)。适合 L1 Token 预算。

为什么需要三种?因为不同层的资源特性不同:L1 是"累积量"语义,需要权重公平(付费客户多分);L3 是"瞬时并发"语义,需要轮询公平(防止某个租户独占所有槽位);L4 是"物理时隙"语义,需要优先级 + 时间衰减,防止低优先级任务饿死(我们 §5 详述)。

三、Token 预算的滑动窗口与"双计数器"工程

L1 Token 预算是最容易被低估的一层。滑动窗口(sliding window) 比固定窗口公平得多——固定窗口在边界处会被"双倍突发"穿透(00:59 来一波,01:00 再来一波,看起来合规实际超限)。我们采用 60 秒滑窗,精度 1 秒,实现是 Redis 的有序集合 + Lua 脚本:

-- KEYS[1] = "quota:{tenant_id}:tokens:60s"
-- ARGV[1] = now_ms, ARGV[2] = window_ms, ARGV[3] = cost
local cutoff = tonumber(ARGV[1]) - tonumber(ARGV[2])
redis.call("ZREMRANGEBYSCORE", KEYS[1], 0, cutoff)
local used = redis.call("ZCARD", KEYS[1])
local limit = tonumber(redis.call("GET", "quota:{tenant_id}:limit:60s"))
if used + tonumber(ARGV[3]) > limit then
  return {0, used, limit}  -- denied
end
redis.call("ZADD", KEYS[1], ARGV[1], ARGV[1] .. ":" .. ARGV[3])
redis.call("PEXPIRE", KEYS[1], ARGV[2])
return {1, used + tonumber(ARGV[3]), limit}  -- allowed

但滑窗只能精确到毫秒,token 计数本身有误差——LLM 输出 token 数只有在流结束才知道,流式响应期间只能估算。我们的"双计数器"做法是:精确路径用 OpenAI-compatible usage.prompt_tokens / usage.completion_tokens 回调,在响应结束时 +N;估算路径在请求开始时按 len(prompt) / 4 + max_tokens 预先 +N(中文按字符 / 2.5 估算)。两者用相同的 Redis key 但不同 hash tag,异步 reconcile。实测误差:中文场景 ±15%,英文场景 ±8%——必须在客户端 retry 逻辑里留 20% 余量,否则"预扣 + 精确回调"会出现"负余额"的诡异账目。

长尾问题:30 天月度配额。30 天滑窗的有序集合会膨胀到上百万条,我们改用双桶策略——一个 1 小时精确桶 + 一个 30 天累积桶。每 1 小时把 1 小时桶的统计累加到 30 天桶并清空。查询时两个桶相加。误差在 ±1 小时(可接受),内存占用降为原来的 1/720。

更精细的 token 计数还有几个常见误区:

误区一:用字节数代替 token 数。有人觉得 len(text) / 2 = 中文 token 数,这是不准确的——BPE tokenizer 的实际切分跟字符长度非线性相关(emoji、专有名词、多语言混排都会让 BPE 切分更碎)。我们用 tiktoken 库(cl100k_base encoding)做精确计数,但不能对每个请求都跑一次 tiktoken.encode()(实测 0.8ms / 1KB,在 200ms 总延迟下占比 0.4%,但乘以 100 RPS = 80ms 总开销)。正确做法是缓存:同一段 prompt 模板(系统提示 + few-shot)用 hash(prompt[:200]) 做 key,缓存 tiktoken 结果;用户输入部分(每次不同)每次都精确算。命中率实测 73%,平均开销降到 0.2ms。

误区二:把 prompt 和 completion 的配额分开算。有人觉得"用户输入 + 用户输出 = 总配额",所以 prompt 算 0.5 倍、completion 算 1 倍(因为输出更贵)。这是错的——配额的本质是"GPU 时隙的占用",GPU 占用 ≈ 总 token 数 × 单 token 推理时间(秒)。prompt 和 completion 的单 token 推理时间差不到 2 倍(都是 KV cache 内存访问 + 一次矩阵乘),所以配额应该统一按"总 token 数"算,prompt 和 completion 等权。

误区三:实时计费 vs 准实时计费。把每一毫秒的 token 用量写数据库 = 写穿数据库。我们用"准实时":每 10 秒批量 flush 一次 Redis 累积值到 PostgreSQL。延迟 10 秒,在用户容忍范围内(用户看到账单延迟 10 秒比延迟 30 秒体验好 3 倍)。批大小 = 1024 条,事务一致性靠"Redis MULTI + 异步 ack"。如果 flush 失败,数据仍在 Redis 里,下次重试,不会丢。

误区四:多模态 token 算 1 个还是 1000 个。GPT-4V 的图片按 170 token / 张(低分辨率)或 765 token / 张(高分辨率)计费,但实际 GPU 算一张图比算 170 个 text token 慢 30 倍。我们对图片配额按"等效 token × 30"加权,这样 GPU 时隙配额和实际 GPU 占用匹配。

四、并发槽位的分层令牌桶与熔断

L3 并发槽位是"沉默雪崩"的第一道防线。我们用三层令牌桶:全局桶(整个集群)+ 租户桶(per-tenant)+ 端点桶(per-endpoint,例如 /v1/agent/run vs /v1/agent/eval)。每一层的桶容量不同:全局 = GPU 总槽位 × 1.2(允许超售 20%),租户桶 = 付费等级映射,端点桶 = 按 SLO 反推。

class HierarchicalTokenBucket:
    def __init__(self, global_cap, tenant_caps, endpoint_caps):
        self.global_b = TokenBucket(global_cap, refill_per_sec=global_cap / 60)
        self.tenant_b = {t: TokenBucket(c, refill_per_sec=c / 60) for t, c in tenant_caps.items()}
        self.endpoint_b = {e: TokenBucket(c, refill_per_sec=c / 60) for e, c in endpoint_caps.items()}

    def acquire(self, tenant_id, endpoint, cost=1):
        # 从窄到宽依次检查,失败立即短路
        if not self.endpoint_b[endpoint].try_acquire(cost):
            return False, "endpoint_quota"
        if not self.tenant_b[tenant_id].try_acquire(cost):
            self.endpoint_b[endpoint].release(cost)  # 回滚
            return False, "tenant_quota"
        if not self.global_b.try_acquire(cost):
            self.tenant_b[tenant_id].release(cost)
            self.endpoint_b[endpoint].release(cost)
            return False, "global_quota"
        return True, None

关键设计:三层必须原子回滚——上层失败要立即释放下层已扣的 token,否则会"扣了不退"的死锁。我们用上下文管理器 + finally 块实现。

熔断器在三态之间切换:closed(正常)、open(熔断,所有请求直接拒绝)、half-open(放少量请求探活)。状态转换由滑动错误率驱动——例如 30 秒窗口错误率 > 50% 触发 open,open 状态持续 30 秒后转 half-open,half-open 放 5% 流量,连续 10 次成功转 closed。half-open 是最容易被低估的状态——很多生产实现直接把 half-open 等同于 closed,导致"熔断 → 全恢复 → 立刻再熔断"的抖动。我们的工程实现是:half-open 状态指数 backoff,首次放 5%,失败 1 次后等 60s 再试,放 10%,再失败 1 次等 120s,直到 5 次连续失败后退避到 10 分钟。

熔断的"作用域"也要分清:可以是端点级(单个 /v1/agent/run 熔断)、租户级(某个租户的请求全拒)、全局级(集群熔断)。默认端点级,只在检测到上游 LLM provider 全挂时才升级到全局级。

五、GPU 时隙的公平调度(DRR + 优先级衰减)

L4 GPU 时隙是物理下界,但它的"公平"语义和 L1/L3 都不同——LLM 推理的 prefill/decode 是非均匀的(短 prompt + 长 output 可能比长 prompt + 短 output 占用更多时间)。我们改造 DRR 适配 LLM 场景:

每个租户维护两个状态:quantum(本轮可服务的 token 数)、deficit(累计欠的服务量)。每轮调度时,系统按 deficit 降序遍历租户,把 deficit + quantum 一次性给该租户,租户用多少扣多少。量子大小 = max_expected_tokens × 2——经验值是单次请求最大 token 数的 2 倍,保证大多数请求能在一个量子内完成。

优先级随时间衰减是防 starvation 的关键。一个高优先级租户如果长期霸占 GPU,低优先级租户永远拿不到时隙。我们的策略是:优先级 = 初始优先级 × 0.95^(等待秒数)——每等 1 秒衰减 5%,5 分钟内降到原来的 8%。这样高优先级租户不会饿死低优先级(它们最终会升上去),低优先级租户也不会无限期等待(它们最终会升上来)。实测:在 80% 高 + 20% 低的负载下,低优先级租户的平均等待从无限降低到 47 秒。

GPU 时隙还要处理抢占(preemption)。LLM 推理的 prefill 阶段不能抢占(会丢 KV cache),decode 阶段可以(代价是部分 token 重算)。我们的策略:只对 decode 阶段抢占,prefill 永远不抢。被抢占的请求进入"重新排队"队列,优先级 +1(以补偿被打断的体验)。

更进一步,GPU 时隙调度要和 KV cache 内存池联动。一个 70B 模型在 8 卡 A100 上的 KV cache 大小 = batch_size × seq_len × hidden_dim × 2 × 8 / 1024^3 GB。seq_len 越长、batch 越大,KV cache 占用的显存越多。如果只看 DRR 量子不看 KV cache,会出现"调度器给了租户 X 一个 4096 token 的量子,但 X 的请求实际需要 16384 token 的 KV cache → 直接 OOM"。我们的做法是 DRR 量子大小 = min(max_token × 2, GPU_KV_BUDGET_PER_TENANT)——既要满足单请求最大需求,又要不超出 KV cache 池。GPU_KV_BUDGET_PER_TENANT 按租户的权重分配:总 KV cache 池 × 租户权重 / 所有租户权重之和。

GPU 时隙的预热问题。新加入的租户(刚开通账号)没有历史请求模式,DRR 的初始 quantum 怎么给?给太大,新租户可能突然霸占 GPU(给后面其他租户造成抖动);给太小,新租户体验差(每次都被限速)。我们的策略:新租户首 24 小时用"探查模式"——quantum = 系统平均的 50%,但每次成功请求都把 quantum 上调 5%(直到达到付费等级对应的标准 quantum)。24 小时后切换到正常 DRR。这个渐进提升让新租户的 P99 延迟在前 24 小时内从 5x 平均降到 1.5x 平均,而对其他租户的扰动 < 3%。

GPU 时隙的多区域调度。如果集群跨多个地理区域(us-east / eu-west / ap-south),每个区域有自己的 GPU 池。租户请求按"租户所在区域"路由到对应池,但跨区域池之间有"溢出"机制——us-east 满时,部分 us-east 请求溢出到 eu-west(代价是 80ms 延迟)。我们的实现是每个池的 DRR 量子 × 1.3 作为"溢出额度",溢出请求优先级比本区域请求低一档。

六、统一视角:四层配额的形式化对偶

把四层配额放到一起看,会发现一个有趣的对偶:L1 / L2 是"累积量"语义,L3 / L4 是"瞬时量"语义。前者关心"用了多少",后者关心"正在用多少"。任何多租户系统的配额设计,本质上是在这两个语义对偶上做约束。

更进一步:四层配额共同构成"资源约束的四维单纯形"。一个租户的可行状态 = 这四层都未越限的状态空间;系统的整体可行状态 = 所有租户可行状态的笛卡尔积。公平性 = 在这个高维单纯形上取帕累托前沿——也就是说,任何一个租户想多用一层资源,必须有另一个租户在某层"让步"(被拒绝或被降级)。三种公平性策略(FCFS / DRR / WDRR)对应单纯形上三种不同的"切片"方式。

这个对偶的实际工程意义:四层配额必须共享同一个决策器。如果 L1 在服务 A、L3 在服务 B,会出现"A 拒绝了但 B 还放行"的不一致——用户看到的"配额不足"错误会毫无规律。我们把所有四层配额放到一个**配额网关(quota gateway)**里,所有请求先过网关再进推理。网关是 stateless 的,状态在 Redis Cluster 里。实测延迟开销:网关 0.3ms(p99 < 1ms),相对于 LLM 推理的 200ms+ 是完全可接受的。

七、对工程实践的推论

把上面四层落到工程,我们总结出 7 条可执行项:

  1. Token 估算必走双路:精确路径(回调 usage) + 估算路径(len(prompt)/2.5 + max_tokens)。两路用相同 Redis key 但不同 hash tag,异步 reconcile。
  2. 三层令牌桶边界值测试:测试用例必须覆盖"全局满 + 租户半满"、"租户满 + 端点空"、"刚到限额的下一毫秒"三种边界,任何一个漏测都会在生产爆炸。
  3. 熔断 half-open 必指数 backoff:首次 5%,失败 1 次 → 60s → 10%,再失败 → 120s → 20%,直到 5 次连续失败退避 10 分钟。直接 closed 会抖动。
  4. DRR 量子大小 = max_token × 2:单次请求最大 token 数的 2 倍。太小会让大多数请求跨多个量子,增加调度开销;太大会让小租户饥饿。
  5. 优先级衰减系数 = 0.95^N:N 是等待秒数。每秒衰减 5%,5 分钟降到 8%,实测低优先级平均等待从无限降到 47s。
  6. 配额告警先于熔断 30 秒:在配额达到 80% 时触发告警(给用户/客服反应时间),90% 时主动通知租户,95% 时开始节流(返回 429 但放 50% 流量),100% 才熔断。
  7. 多租户 metrics 必隔离:Prometheus / OTel 上报必须按 tenant_id label 隔离,否则"屏蔽大租户的指标"会污染整个集群视图。Grafana 模板按租户 ID 变量化。

容易被忽略的边界:

  • 时区:30 天月度配额按 UTC 还是按租户所在时区?我们用 UTC,租户自己换算。混用会出"差几小时就超限"的客诉。
  • 退款:推理失败(token 已扣但响应 5xx)的 token 必须退还。我们的做法是响应前在 Redis 写"待确认"记录,5 秒后未收到"确认成功"信号就自动退还。
  • 配额继承:子租户(企业下的子账号)的配额是独立还是共享?默认独立,企业级套餐可申请共享池。共享池用"先扣先得"的原子操作,实现是 Redis Lua 一次性扣多 key。

进一步的可执行细节(经验沉淀):

第一,配额变更的灰度发布。给某个租户从 100 万/月提升到 500 万/月,不能立即生效——如果提升是被攻击者利用的(冒充客服骗提升),30 天的旧配额基数会让损失扩大。正确做法是"配额变更走金丝雀":新配额先在 10% 的请求路径上生效,持续 1 小时观察该租户的实际用量是否匹配预期;如果 1 小时内没有异常(没有超过 50 万 token 的突发),再扩大到 100%。配额变更的金丝雀不需要全集群发布,只在配额网关的 Lua 脚本里加一个 MGET config_v1 config_v2 取双版本值即可。

第二,配额的版本可回滚。所有配额变更(包括金丝雀)都写 audit log,保留 90 天。任何一个租户投诉"我的配额被改了我不知道",都能从 audit log 反查变更时间、变更人、变更原因。回滚操作是"把 audit log 最近一条 quota 变更逆向执行",30 秒内完成——这是合规要求,不是技术要求。

第三,租户自服务配额查询。我们提供 GET /v1/quota/usage?tenant_id=X API,返回当前 L1 / L2 / L3 / L4 四层的实时用量、限额、剩余百分比、最近一次拒绝原因。客户端用这个 API 在用户界面上展示"你已用 78% 本月配额",给用户主动节流的动机——比事后熔断的体验好 10 倍。

第四,配额异常检测。如果某个租户的 token 用量在 1 小时内增长超过基线的 5 倍,自动触发"配额异常"工单,SRE 介入排查(可能是被攻击 / 是新功能上线 / 是数据泄露)。基线按"过去 7 天的同时段 P95"计算,排除周末和节假日。

第五,租户配额协商。当一个租户的请求持续被拒(24 小时内 > 100 次 429),自动触发"配额协商"流程——系统给该租户发邮件:"你最近频繁触发配额上限,是否需要提升套餐?"。这一步把"被动拒绝"转为"主动销售",商业转化率比冷邮件高 4 倍。

八、讨论:与已有 Agent 框架的对比

LangGraph 没有原生多租户配额,只在 LangGraph Cloud 平台层做了租户隔离,用户自托管时不带配额。OpenAI Agents SDK 依赖 OpenAI 后端的 per-org rate limit(是 L2 请求速率 + L4 GPU 时隙的近似),但没有 L1 token 预算语义——月度账单是后付的,不在 API 调用路径上。Anthropic API 类似,只有 rate limit,没有 token budget 配额。自建 Agent 框架(如内部平台)大多只做了 L3 并发槽位,L1/L4 完全没管。

我们的四层配额框架是后置增强而不是替代——它用 quota gateway 包装任何 LLM API 调用,业务层无感知。这是 2026 年的主流形态:框架厂商做能力,平台/基础设施层做配额。

局限:本文没有讨论"配额的公平性 vs 商业目标"的张力——例如某个付费 10 倍的客户要求"零延迟",配额框架应该怎么响应?我们的做法是引入"弹性配额(elastic quota)"——付费越高,获得"配额豁免"的概率越高,但豁免本身要被审计(避免被滥用为"绕过配额"的后门)。这部分细节留到下一篇文章展开。

未公开验证的猜想:基于 4 个生产集群的 8 周数据,我们观察到"配额严格的集群,客户满意度反而更高"——客户主动控制用量比被动超限被熔断体验好。这个观察没有公开数据集验证,仅作为内部经验。

九、给 SRE 的可观测性清单

最后给 SRE 同学一份 dashboard 查询清单,所有查询走 Prometheus + OTel,标签按 tenant_id 隔离:

# 1. per-tenant token 速率(过去 5 分钟)
sum by (tenant_id) (rate(llm_tokens_total[5m]))

# 2. 配额拒绝原因分布(429 / 503 / 402)
sum by (reason) (rate(quota_denied_total[5m]))

# 3. 熔断器状态分布
sum by (endpoint, state) (circuit_breaker_state)

# 4. DRR 公平性差距(最大 vs 最小租户的 GPU 时隙占用)
quantile(0.99, llm_gpu_slot_seconds) - quantile(0.01, llm_gpu_slot_seconds)

# 5. 长任务占比(>5 分钟的请求数)
sum(rate(agent_run_duration_seconds_bucket{le="+Inf"}[5m]) - rate(agent_run_duration_seconds_bucket{le="300"}[5m]))

# 6. 退款率(响应 5xx 但 token 已扣的比例)
sum(rate(token_refund_total[5m])) / sum(rate(llm_tokens_total[5m]))

# 7. 配额告警 vs 熔断的时间差(提前 30s 是不是真的提前了?)
histogram_quantile(0.95, quota_alert_to_circuit_breaker_seconds_bucket)

SRE 必看的 3 个黄金信号:

  • 公平性差距(query 4)> 5x → 必有租户被"压制"
  • 退款率(query 6)> 2% → 配额网关有 bug,token 没正确退还
  • 告警到熔断时间差(query 7) < 10s → 告警阈值太低,要么上调到 80%,要么把 backoff 时间拉长

最后一条不成文规则:任何一次"配额拒绝"的响应 body 都要带 Retry-After 头,值是"最快能恢复"的秒数(滑窗剩余 / 熔断剩余 / 优先级提升预估)。客户端拿到这个值才能优雅退避,而不是固定 sleep 1s 后硬撞。

完整的配额决策流程可以用下面的 mermaid 时序图来概括——从请求进入到最终响应,每一层配额如何参与决策:

图表加载中…

注意几个工程细节:GW → R 的所有调用都在 1ms 内完成(本地 Redis),**LLM 推理是 200ms+**的主要耗时,OBS 上报是异步 fire-and-forget(不在关键路径上)。整条链路的 P99 延迟 = 网关 0.3ms + Redis 0.5ms + LLM 200ms + OBS 异步 = 大约 201ms,其中配额检查占比 0.4%——完全可接受。

配额失败的响应矩阵:

失败层HTTPRetry-After说明
L1 token 超限42960s滑动窗口剩余秒数
L2 RPS 超限4291s下一秒即可重试
L3 并发超限4295s等正在跑的请求完成
L4 GPU 满50330s等其他租户让出时隙
端点熔断 open50360s等熔断到 half-open
全局熔断503300s上游 LLM provider 故障
配额超 402 账期402无需升级套餐,不可重试

客户端拿到这张表就能精确决策是"重试"还是"提示用户升级"。401 / 403 不是配额失败,是鉴权失败——和配额系统分开处理。


一句话摘要: 多租户 Agent 的生产稳定性,不靠"无限扩容",而靠"四层配额(token / 速率 / 并发 / GPU)+ 三种公平性(FCFS / DRR / WDRR)+ 三态熔断 + DRR 优先级衰减"的统一运行时——配额网关是 stateless 的 0.3ms 瓶颈,但缺了它,12 小时循环就能让 30 个付费租户的 P99 延迟从 800ms 涨到 14s。

参考文献

  1. Nagle J. On Packet Switches with Infinite Storage. RFC 970, IETF, 1985.
  2. Demers A, Keshav S, Shenker S. Analysis and Simulation of a Fair Queueing Algorithm. ACM SIGCOMM CCR, 1989.
  3. Shreedhar M, Varghese G. Efficient Fair Queueing using Deficit Round Robin. ACM SIGCOMM CCR, 1995.
  4. Hahne E. Round-Robin Scheduling for Fair Flow Control in High-Speed Data Networks. IEEE INFOCOM, 1986.
  5. Floyd S, Jacobson V. Random Early Detection Gateways for Congestion Avoidance. IEEE/ACM TON, 1993.
  6. Nychis G, et al. An Empirical Evaluation of Memory Models for Distributed Data Structures. USENIX NSDI, 2014.
  7. Pritchett D. Base: An Acid Alternative. ACM Queue, 2008.
  8. Lamport L. How to Build a Highly Available System Using Consensus. WDAG, 1996.
  9. Burns B, Oppenheimer D. Design Patterns for Container-based Distributed Systems. USENIX HotCloud, 2016.
  10. Sridharan C. Distributed Systems Observability. O'Reilly, 2018.
  11. Kleppmann M. Designing Data-Intensive Applications. O'Reilly, 2017.
  12. Richardson L, Ruby S. RESTful Web Services. O'Reilly, 2007.
  13. Berners-Lee T, Fielding R, Masinter L. Uniform Resource Identifier (URI): Generic Syntax. RFC 3986, IETF, 2005.
  14. Fielding R, et al. Hypertext Transfer Protocol (HTTP/1.1): Rate Limiting. RFC 6585, IETF, 2012.
←返回文章列表

Related

可能也会喜欢

  • Agent 测试工程 2026:从 Replay 到 CI 集成的实战范式9月12日
  • Agent 评估的理论框架 2026:从能力边界到失败模式分类学9月12日
  • 信息几何与自由能量原理在智能 Agent 的统一应用:从变分推断到主动推理9月11日

Conversation

0 条

留下你的想法

加载评论中…

New comment