AWS API 限流机制 - 令牌桶算法与 429 错误的本质
解析 AWS API 速率限制通过令牌桶算法实现的机制、突发容量的概念、各服务限制值的差异,以及避免限流的实践对策。
为什么 AWS 对所有 API 设置速率限制
AWS 的所有 API 都设置了按账户、按区域的速率限制(限流)。超出限制时的响应因服务而异,大致分为三类。第一,EC2 的 API 以 HTTP 503(Service Unavailable)返回 RequestLimitExceeded。第二,API Gateway 返回 HTTP 429(Too Many Requests)。第三,许多 AWS 服务的 API 以 HTTP 400(Bad Request)返回 ThrottlingException。也就是说,如果简单地认为「限流一定以 429 返回」,就会漏掉以 503 或 400 返回的那些类型。重试判断不应只看 HTTP 状态码,而应结合错误代码(RequestLimitExceeded / ThrottlingException / TooManyRequestsException 等)一起进行,这样更安全。速率限制有两个目的。第一,确保多租户环境的公平性。如果一个账户大量调用 API,会影响共享同一基础设施的其他账户的性能。速率限制是防止「嘈杂邻居问题」的护栏。第二,保护客户自身。应用程序 Bug 导致无限循环、每秒调用 API 数万次的情况时有发生。没有速率限制,这种失控会导致高额账单。速率限制作为安全网,能够及早检测意外失控。部分服务的速率限制值可在 Service Quotas 中查看,并可通过配额提升请求申请上调。但并非所有服务、所有限制都能统一地查看和上调。像 EC2 的 API 限流值那样,有些限制需要提出请求获得访问权限后才能查看当前值或申请上调,因此在设计时要采取两步走的思路:「Service Quotas 中有的就在那里确认,没有的则通过 Support 确认」。
令牌桶算法的工作原理
AWS 的 API 限流通过令牌桶算法实现。该算法的机制是:令牌(许可证)以固定速率补充到桶(容器)中,每个 API 请求消耗一个令牌。桶空时请求被拒绝。来看实际数值(以下为 2026 年 8 月时点官方文档记载的数值,可能因账户或区域而异)。EC2 的 API 限流文档显示,不带过滤条件也不带分页的非变更操作(如 DescribeInstances)的请求令牌桶最大容量为 50 个令牌,补充速率为每秒 10 个令牌;其他标准非变更操作的最大容量为 100 个令牌,补充速率为每秒 20 个令牌。以 DescribeInstances 为例,在桶满的状态下可以瞬间发送 50 个请求(突发)。用尽之后受补充速率制约,稳定在每秒 10 个请求的节奏。这里要把握的是两者的分工。桶的最大容量决定突发时一次能发出的量,补充速率决定稳态下允许的速率。如果把「突发 = 容量」「稳态 = 补充速率」这一对应关系弄反,估算就会偏差数倍。突发容量是吸收短时间激增的缓冲。在应用启动时集中调用 API 的场景中,起作用的正是这一容量。相反,在长时间持续运行的批处理中,容量只在最初一瞬间起作用,实际吞吐量由补充速率决定。
各服务不同的限流粒度
限流粒度因服务而异。EC2 的 API 为每个 API 操作设置了独立的速率限制。DescribeInstances 和 RunInstances 由不同的桶管理,因此 DescribeInstances 的限流不会影响 RunInstances。此外,EC2 还有与请求速率限制不同维度的「资源速率限制」。像 RunInstances 这样创建或更改资源的操作,除了消耗请求次数的桶之外,还会消耗资源令牌桶。以 RunInstances 为例,资源令牌桶的最大容量为 1,000 个令牌,补充速率为每秒 2 个令牌(2026 年 8 月时点官方文档记载的数值)。重要的是,这个桶不是按 API 调用次数减少,而是按操作对象的资源数量成比例减少。一次 RunInstances 启动 100 个实例就会消耗 100 个资源令牌,因此即便调用次数只有几次,也可能在资源速率限制这一侧被限流。在进行大量启动、大量删除的自动化中,务必把这第二个维度纳入估算。而 DynamoDB 的限流在表级别应用。超过表的预置容量(RCU/WCU)的请求会被限流。这与 API 级别的限流不同,是数据访问的吞吐量限制。Lambda 的并发执行数限制也是限流的一种。账户并发执行数的默认配额为 1,000,但新创建的账户会以降低后的配额起步,并由 AWS 根据使用量自动上调(2026 年 8 月时点官方文档记载)。因此,如果按「新账户也能一开始就达到 1,000」的前提来设计,就会在负载测试中遇到意料之外的限流。当前值最可靠的确认方式是查看 Service Quotas。API Gateway 在账户级别有每秒 10,000 个请求(默认)的速率限制,还可以按 API、按阶段、按方法设置独立的限流配置。通过这种多层限流,可以控制特定 API 端点的集中访问不影响其他端点。
指数退避与抖动 - SDK 自动执行的重试策略
收到限流错误时的正确处理方式是结合指数退避(Exponential Backoff)和抖动(Jitter)的重试。指数退避是将重试间隔按 1 秒、2 秒、4 秒、8 秒指数递增的策略。这样可以逐步减轻对限流中服务的请求压力。抖动是在重试间隔中加入随机波动的方法。仅使用指数退避时,同时被限流的多个客户端会在相同时间重试,再次触发限流,产生「惊群」问题。加入抖动可以分散重试时机。AWS SDK 在内部自动实现了这一重试策略。AWS SDK for JavaScript v3 的默认值是「最多 3 次尝试」,含义是 1 次初始请求加最多 2 次重试。如果误读为「重试 3 次」,估算出的尝试次数就会比实际多 1 次,需要注意。也有例外:DynamoDB 和 DynamoDB Streams 的客户端提供了更积极的设置,即 4 次尝试、基础退避 25 毫秒(这是 2026 年新重试体系下的数值,正从可选启用的设置逐步过渡为默认值)。重试行为并非特定语言才有的话题,作为 AWS SDK 的通用设置定义了 standard / adaptive / legacy 三种模式。当前一代的 SDK 和 CLI 默认使用 standard,已从以前的 legacy 模式迁移过来。adaptive 是客户端自行学习并收紧发送速率的模式,是限流已成常态的处理任务的一个选项。而且 standard 和 adaptive 中内置了称为「retry quota」的总量控制。这是 SDK 客户端内部持有的令牌桶:每次重试都会消耗令牌,请求成功时再逐渐恢复。令牌耗尽时重试即被中止。也就是说,本文主题的令牌桶不仅用于服务端的速率限制,也作为客户端避免滥发重试的机制在使用。不使用 SDK 直接调用 API 时,需要自行实现这些重试机制。
预防限流的设计模式
与其在限流发生后重试,不如从设计上避免限流发生。第一种模式是减少 API 调用。与其每秒调用 EC2 的 DescribeInstances 监控实例状态,不如使用 EventBridge 事件(EC2 Instance State-change Notification),仅在状态变化时接收通知。从轮询转向事件驱动可大幅减少 API 调用次数。第二种模式是利用缓存。不经常变化的信息(账户设置、区域列表等)在本地缓存以减少 API 调用。第三种模式是利用批量 API。DynamoDB 的 BatchGetItem 可在一次 API 调用中获取最多 100 个项目。比单独调用 100 次 GetItem 减少 99% 的 API 调用次数。S3 的 ListObjectsV2 也可通过 MaxKeys 参数在一次请求中获取最多 1,000 个对象。
参考资料(AWS 官方)
本页的第一手信息来源是 AWS 官方网站及官方文档。最新的规格与价格请以下列官方页面为准。
如本页内容与官方文档不一致,请以官方文档为准。