AWS API 限流机制 - 令牌桶算法与 429 错误的本质
解析 AWS API 速率限制通过令牌桶算法实现的机制、突发容量的概念、各服务限制值的差异,以及避免限流的实践对策。
为什么 AWS 对所有 API 设置速率限制
AWS 的所有 API 都设置了按账户、按区域的速率限制(限流)。超出限制的请求会返回 HTTP 429(Too Many Requests)或 503(Service Unavailable)错误。速率限制有两个目的。第一,确保多租户环境的公平性。如果一个账户大量调用 API,会影响共享同一基础设施的其他账户的性能。速率限制是防止「嘈杂邻居问题」的护栏。第二,保护客户自身。应用程序 Bug 导致无限循环、每秒调用 API 数万次的情况时有发生。没有速率限制,这种失控会导致高额账单。速率限制作为安全网,能够及早检测意外失控。各服务的速率限制值可在 Service Quotas 中查看,许多限制可通过配额提升请求进行上调。
令牌桶算法的工作原理
AWS 的 API 限流通过令牌桶算法实现。该算法的机制是:令牌(许可证)以固定速率补充到桶(容器)中,每个 API 请求消耗一个令牌。桶空时请求被拒绝。以具体示例说明:假设 EC2 的 DescribeInstances API 速率限制为每秒 100 个请求,突发容量为 200。桶每秒补充 100 个令牌,桶的最大容量为 200 个。正常情况下桶是满的(200 个令牌),因此可以瞬间发送 200 个请求(突发)。之后只能以每秒 100 个请求的速度处理。突发容量是吸收短时间激增的缓冲。在应用启动时集中调用 API 的场景中,突发容量非常重要。突发用尽后,将被限制在稳态速率(每秒 100 个请求)。
各服务不同的限流粒度
限流粒度因服务而异。EC2 的 API 为每个 API 操作设置了独立的速率限制。DescribeInstances 和 RunInstances 由不同的桶管理,因此 DescribeInstances 的限流不会影响 RunInstances。而 DynamoDB 的限流在表级别应用。超过表的预置容量(RCU/WCU)的请求会被限流。这与 API 级别的限流不同,是数据访问的吞吐量限制。Lambda 的并发执行数限制也是限流的一种。超过账户默认并发数 1,000 的函数调用会以 429 错误被限流。API Gateway 在账户级别有每秒 10,000 个请求(默认)的速率限制,还可以按 API、按阶段、按方法设置独立的限流配置。通过这种多层限流,可以控制特定 API 端点的集中访问不影响其他端点。
指数退避与抖动 - SDK 自动执行的重试策略
收到限流错误(429)时的正确处理方式是结合指数退避(Exponential Backoff)和抖动(Jitter)的重试。指数退避是将重试间隔按 1 秒、2 秒、4 秒、8 秒指数递增的策略。这样可以逐步减轻对限流中服务的请求压力。抖动是在重试间隔中加入随机波动的方法。仅使用指数退避时,同时被限流的多个客户端会在相同时间重试,再次触发限流,产生「惊群」问题。加入抖动可以分散重试时机。AWS SDK 在内部自动实现了这一重试策略。SDK v3(JavaScript)默认最多重试 3 次,应用指数退避和完全抖动。boto3(Python)也具有相同的重试策略。不使用 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 个对象。如需系统学习 API 设计和限流对策,专业书籍 (Amazon) 可作为参考。