梳理AWS Regional/Per-account/Per-stage/Per-client四级限流配置及嵌套关系,指出429响应无法区分来源的问题,帮助开发者正确理解实际遇到的限流瓶颈。
从一个 API Gateway 端点返回 429 Too Many Requests 时,并不能告诉你这个限流是由四个中的哪一个造成的。它们有层级关系,其中三个在响应中是不可见的。
AWS 文档记录了四种不同的限流相关设置,将它们混为一谈正是造成困惑的原因:
AWS 区域限流(Regional throttling limits) — 作用于某个区域内所有账户和客户端。由 AWS 设置,对客户不可见、不可更改。没有任何配置可以超出这个上限。
单账户限流(Per-account limits) — 作用于你账户在一个区域内的每个 API。这就是人们说“API Gateway 限制”时所指的那个。可通过请求调整,但永远不会超过区域限流。
单 API、单阶段限流(Per-API, per-stage limits) — 在某个阶段的 method 级别生效。你可以设置一个值作用于所有 method,也可以为每个 method 设置不同值。不能超过 AWS 的限制。
单客户端限流(Per-client limits) — 作用于通过附在 Usage Plan 上的 API Key 识别的调用方。不能超过单账户限制。
每一层都是一个令牌桶(token bucket),这就是为什么每个层级都有两个数字而不是一个。Rate 是令牌补充的速度,单位是每秒请求数。Burst 是桶的容量 —— API Gateway 在开始返回 429 之前可以满足的最大并发提交数。Burst 大于 rate 才能让尖峰流量不经整形直接通过;burst 为零则意味着 rate 成为一个没有任何容差的硬上限。AWS 明确表示限流和配额都是基于尽力而为(best-effort)原则应用的,应该被视为目标而非保证的上限,因此偶尔出现任意方向的超出是文档中记录的行为而非 bug。
AWS 文档精确记录了评估顺序,而且与大多数人的假设相反 —— 最窄的限制最先检查:
实际的理解方式是:你在某条路由上设置的每方法限制保护的是该路由不受账户限制的影响,但并不能保护该路由不被账户中其他所有共享同一个桶的服务先用光配额。这正是让 AI 端点踩坑的不对称问题。你的模型路由是低流量、高成本的;同一账户和区域中的其他某个 API 是高流量、低成本的;低成本的先耗尽了账户桶,高成本的开始返回 429,而它本身没有任何变化。解决方案不在 AI 端点本身 —— 而是要在那个产生干扰的邻居上设置每方法限制。
AWS 文档记录的默认账户级限流为每个账户每个区域每秒 10,000 请求,这个配额在 HTTP API、REST API、WebSocket API 和 WebSocket 回调 API 之间共享,令牌桶容量为 5,000 请求。这句话中有两个细节在起作用。首先,WebSocket 回调流量是计算在内的 —— 在流式中继中每次 @connections 发送都占用与普通请求相同的桶,这就是为什么要缓冲令牌而不是逐个发送。其次,AWS 表示 burst 配额由服务团队根据你的整体 RPS 配额设置,客户无法控制或申请更改,即使 rate 是可以调整的。
默认值也不是统一的。AWS 文档记录了在特定区域列表中降低的默认值为每秒 2,500 请求、burst 1,250 —— 包括非洲(开普敦)、欧洲(米兰)、亚太(雅加达)、中东(阿联酋)、亚太(海得拉巴)、亚太(墨尔本)、欧洲(西班牙)、欧洲(苏黎世)、以色列(特拉维夫)、加拿大西部(卡尔加里)、亚太(马来西亚)、亚太(泰国)和墨西哥(中部)。一个在 us-east-1 表现正常、在 eu-south-2 以四分之一负载就开始限流的部署并不奇怪。
这两个数字和区域列表均来自撰写本文时的 Amazon API Gateway quotas 页面。尤其是区域列表会随着 AWS 上线新区域而增长 —— 请查阅该页面确认最近上线的区域。
AWS 文档记录 REST API 集成超时为 50 毫秒到 29 秒。对于一个同步的模型调用来说,这是一个非常紧张的上限 —— 一次长生成,或者路由后面冷启动的容器,都会超过这个时间并返回 504,而模型仍在运行、仍在计费。
自 2024 年 6 月起,AWS 允许通过 Service Quotas 中名为“Maximum integration timeout in milliseconds”的条目来提高这个上限,但仅适用于区域 REST API 和私有 REST API —— edge-optimized REST API 和 HTTP API 不符合条件。AWS 在同一公告中指出,提高上限可能需要降低你的账户级限流配额。这就是从本页要记住的那句话:在一个混合了多种 API 类型的账户中,为一个慢速 AI 路由购买空间会降低其他所有东西的请求上限。当工作确实需要几分钟时,更便宜的答案是停止让它同步执行 —— 返回一个 job id,在 Step Functions 工作流后面做这项工作,然后流式推送或轮询结果。
响应体不会告诉你,但访问日志会,因为 API Gateway 将原因暴露为一个上下文变量。在阶段上启用访问日志,并使用包含网关响应类型和消息的格式:
{
"requestId": "$context.requestId",
"ip": "$context.identity.sourceIp",
"apiKeyId": "$context.identity.apiKeyId",
"routeKey": "$context.routeKey",
"status": "$context.status",
"responseType": "$context.error.responseType",
"errorMessage": "$context.error.message",
"integrationLatency": "$context.integrationLatency"
}
responseType 为 THROTTLED 意味着速率或 burst 限制被超出。QUOTA_EXCEEDED 意味着 Usage Plan 的请求配额 —— 每天或每月的限额,这是另一种机制,也返回 429。这两个对客户端来说看起来一样,但修复方法完全不同:一个是想要更高的速率,另一个是想要不同的 plan。
除了日志,还要关注阶段级别的 CloudWatch 指标。4XXError 与 Count 的比率告诉你有多少流量被拒绝,IntegrationLatency 与 Latency 的对比可以将后端耗时与网关耗时分开。如果 Latency 高而 IntegrationLatency 持平,说明队列在你的代码前面,而不是在里面。
AI 端点在串联中有两个独立的限流器,它们返回相同的状态码:前面的 API Gateway 的 429,以及后面模型提供商自己的限流 —— Bedrock 的 ThrottlingException,或者上游自己的 429 及相应的 reset headers。区分它们很重要,因为前者需要背压(backpressure),而后者需要故障转移到不同的模型或区域。像 Multigrid 这样的网关位于第二个边界上:它读取每个提供商的限流信号、重试或路由到其他地方,并呈现一个一致的错误,这样前端可以区分“你发送太快了”和“模型已满”。
Fixing ThrottlingException on Amazon Bedrock
Private Integration Between API Gateway and a VPC Model Endpoint
WebSocket Streaming Through AWS API Gateway