常见限流错误码解析:ThrottlingException 等

限流报错,说白了就是 KMS 跟你说「慢点,扛不住了」。我自己排查故障时,最先看的就是错误码。不同错误码背后是不同限流策略,搞混了会走弯路。这一章我把常见的限流错误码梳理清楚,再讲讲实战怎么处理它们。

限流错误码全家桶

KMS 限流相关的错误码有好几个,但最核心的就那三四个。我整理了一张表,方便你对照排查:

错误码HTTP 状态码含义常见触发场景应对策略
ThrottlingException429请求速率超过账户级或 API 级限制批量加密、频繁 CreateKey等待 Retry-After 后重试,使用退避算法
LimitExceededException400资源数达到上限(如密钥数)创建第 1000 个 CMK删除旧资源或申请增加配额
TooManyRequestsException429并发请求过多(某些 SDK 独有)高频调用 Decrypt减少并发数,增加本地缓存
ServiceUnavailable (503)503服务过载,临时性限流突增流量重试 + 退避,考虑熔断
RequestLimitExceeded429(极少见)API 调用速率超限批量调用 ListKeys与 ThrottlingException 同处理
我的习惯:收到 429 后,先看响应头的 Retry-After(秒数)或 x-amz-retry-after(毫秒),按那个值等待最准。AWS 的 KMS SDK 内部已经用了指数退避,但你自己手写的调用代码容易忽略这个头。

ThrottlingException 深度解析

这是限流故障里出现频率最高的错误码,没有之一。我最早遇到它是在一个日志加密项目里,批量加密时瞬间并发拉满,结果一大片错误飞回来了。嗯,当时还是有点慌的。

为什么它会频繁出现?因为 KMS 的限流分两层:账户级和API 级。账户级就是整个账号每秒最多处理多少请求,API 级则是每个接口(比如 GenerateDataKey)有自己的配额。ThrottlingException 通常指的是账户级限流。

举个实战场景:你用 KMS::Encrypt 连续加密 1000 个数据块,每个调用单独发送。假设账户级限流是 500 QPS,那你第 1000 个请求极大概率收到 ThrottlingException。这时候 SDK 会帮你自动重试吗?不一定。有些语言 SDK 默认只重试 3 次,而且重试逻辑可能没考虑抖动。我们来看一段代码:

import boto3
from botocore.config import Config

config = Config(
    retries={'max_attempts': 5, 'mode': 'adaptive'}
)
kms = boto3.client('kms', config=config)

try:
    response = kms.encrypt(
        KeyId='alias/my-key',
        Plaintext=b'sensitive data'
    )
except ClientError as e:
    if e.response['Error']['Code'] == 'ThrottlingException':
        # 手动读取 Retry-After
        retry_after = e.response['ResponseMetadata']['HTTPHeaders'].get('retry-after')
        if retry_after:
            time.sleep(int(retry_after) + random.random() * 0.1)
    raise
我曾经踩过的坑:有一个线上服务,客户端收到 ThrottlingException 后没处理 Retry-After,直接按固定 1 秒间隔重试。结果集群所有节点同时重试,又触发了限流,形成死循环。最后服务挂了半小时。记住——重试必须加 随机抖动,最好用指数退避。

其他常见限流错误码对比

光知道 ThrottlingException 还不够,我遇到过很多同学把 LimitExceededException 当限流处理,结果无限重试也没用。为什么?因为这是配额问题,不是速率问题。

  • LimitExceededException —— 你创建了太多密钥(默认可创建 1000 个 CMK)。处理方式是删密钥或提工单加配额,重试没有任何意义。
  • TooManyRequestsException —— 一些 SDK 把并发层限流单独拎出来。比如你用 100 个线程同时调用 Decrypt,即使总 QPS 不超也可能触发这个。解决方法是改用连接池或控制并发数。
  • ServiceUnavailable —— 这个错误码有时不是限流,是服务真的宕了。但 KMS 限流严重时也会返回 503。我一般和 429 一样处理:退避重试,但增加熔断逻辑(比如连续 3 次 503 就暂停 10 秒)。

错误码背后的限流维度

KMS 限流不是「一刀切」,它分几个维度。我画了一张流程图,帮你理解收到错误后该怎么定位:

限流错误码排查流程图 收到错误响应 解析 Error.Code 是限流错误码? 是 读取 Retry-After 指数退避 + 抖动 重试 否 按其他错误处理 💡 说明: 1. 限流错误码包括:ThrottlingException、TooManyRequestsException、RequestLimitExceeded 2. LimitExceededException 属于配额错误,不应在此流程中重试 3. ServiceUnavailable(503)也建议采用退避重试,但需监控熔断 4. Retry-After 头是精确等待时间,优先使用;没有则按默认退避策略

实战小贴士:错误码处理最佳实践

结合上面的流程,我总结几个实操要点:

  1. 先区分「限流」和「配额」:Throttling 类错误才退避重试,LimitExceeded 类错误去查 Console 或工单。
  2. 一定用指数退避 + 随机抖动:不能只写 time.sleep(1),那是自杀式重试。我推荐底层基数为 2,上限 30 秒。
  3. 读取 Retry-After 头:很多云厂商在这个头里告诉你具体等多久。我见过一个项目,错误码是 ThrottlingException,响应头里回了 5 秒,但客户端硬等了 30 秒,白白浪费时间。
  4. 加业务层面的降级:限流频繁时可以考虑本地缓存结果(比如 Decrypt 结果缓存 5 分钟),减少对 KMS 的直接调用。
  5. 监控限流指标:在 CloudWatch 里设置 ThrottledCount 指标告警,限流超过阈值时自动发通知。我自己习惯每 5 分钟检查一次,如果连续两次阈值超过 10%,就启动降级。

一句话总结:
收到 429/503 → 解析错误码 → 是限流类 → 读 Retry-After → 指数退避+抖动重试;
是 LimitExceeded → 查资源数、删密钥或加配额;
其他错误 → 按对应文档处理。