API Key 与模型限流
限流可以保护模型服务提供方容量,并防止单个调用方或模型耗尽共享配额。当配额属于某个调用方时,请在调用方 API Key 上配置限制;当使用某个模型的所有调用方应共享配额时,请在模型上配置限制。
这些限制属于其所配置的 API Key 或模型。限流策略是独立规则,可以匹配团队、成员、API Key、模型、模型名称或模型服务提供方,并把匹配流量划分到彼此独立的配额桶中。同一请求可以同时匹配 API Key 限制、模型限制和策略。每个匹配的限制都会生效;只要有任一配额桶耗尽,网关就会在调用模型服务提供方之前返回 429。
本指南介绍如何在 API Key 和模型上配置限制、验证限制是否生效,以及选择共享计数器存储。AISIX Enterprise 或 AISIX Cloud 使用控制面路径,开源 AISIX 网关使用资源文件路径。两种路径配置相同的网关能力。
准备工作
开始前,请准备以下内容:
- 可以发送代理请求的模型别名和调用方 API Key。如果尚未配置,请参阅模型服务提供方密钥、模型别名和调用方 API Key。
- 对于 AISIX Enterprise 或 AISIX Cloud:已接入网关的环境,以及具有写权限范围的 Admin Token。可以按照 AISIX Enterprise 快速入门进行本地评估;如需申请 AISIX Cloud 访问权限,请联系 API7。
- 对于开源 AISIX 网关:可验证并重新加载的声明式资源文件。参见重新加载资源文件。
导出用于验证本页任一示例的网关请求参数:
# AISIX_PROXY 不含尾部斜杠或端点路径
# 本地快速入门使用 http://127.0.0.1:3000。
export AISIX_PROXY="YOUR_AISIX_GATEWAY_URL"
export AISIX_MODEL="gpt-4o-prod"
export AISIX_API_KEY="YOUR_CALLER_API_KEY"
对于控制面示例,还需导出控制面连接参数及待限制资源的 ID:
# AISIX_CP 包含 /api,且不含尾部斜杠
# 本地 AISIX Enterprise 快速入门使用 http://localhost:8080/api
export AISIX_CP="YOUR_AISIX_CONTROL_PLANE_API_URL"
export AISIX_TOKEN="YOUR_ADMIN_TOKEN"
export ENV_ID="YOUR_ENVIRONMENT_ID"
# API_KEY_ID 标识明文值为 AISIX_API_KEY 的调用方 Key。
export API_KEY_ID="YOUR_CALLER_API_KEY_ID"
# MODEL_ID 标识别名为 AISIX_MODEL 的模型。
export MODEL_ID="YOUR_MODEL_ID"
选择限流配置方式
可以通过两种方式配置限流:
- 把
rate_limit块直接关联到调用方 API Key 或模型。当某个应用或租户需要独立配额时使用调用方限制;当某个别名的所有调用方应共享配额时使用模型限制。 - 当需要单独管理的配额、条件匹配,或需要为团队、成员、API Key、模型或模型服务提供方创建独立配额桶时,请创建限流策略。
调用方 API Key 还可以为其调用的每个 MCP 服务器设置单独的请求数和并发限制。参见 MCP 限流与预算。
每个匹配的限制都会生效。例如,一个请求可以同时占用调用方 API Key、解析后的模型、团队策略和成员策略中的容量。只有所有匹配的配额桶都有剩余容量,请求才会继续。
配置调用方 API Key 限制
以下示例把一个调用方 API Key 限制为每分钟一次请求。
AISIX Enterprise 或 AISIX Cloud
在控制台中打开 API keys,展开调用方 API Key,然后使用其中的 Rate limits 控件。Admin API 为自动化提供相同配置能力。
更新调用方 API Key,为其设置每分钟一次请求的限制:
curl -sS -X PATCH "${AISIX_CP}/environments/${ENV_ID}/api_keys/${API_KEY_ID}" \
-H "Authorization: Bearer ${AISIX_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"rate_limit": {
"rpm": 1
}
}'
更改会自动投射到已接入的网关。
开源 AISIX 网关
替换现有调用方 API Key 条目以添加相同限制。保留其当前的 display_name、key_env 和 allowed_models 值:
api_keys:
- display_name: chat-app
key_env: CALLER_API_KEY
allowed_models:
- gpt-4o-prod
rate_limit:
rpm: 1
让验证命令可以访问调用方凭证:
# 使用与验证请求相同的明文值。
export CALLER_API_KEY="${AISIX_API_KEY}"
如果 AISIX 安装在主机上,请在重新加载网关之前验证完整资源文件:
aisix validate --resources resources.yaml
对于开源快速入门中的 Docker 环境,请使用基于容器的验证和重新加载命令。快速入门已让容器可以访问 CALLER_API_KEY。
配置模型限制
当某个别名的所有调用方都应共享同一配额时,请使用模型限制。以下示例允许使用 gpt-4o-prod 的所有调用方 API Key 每分钟总计发送一次请求。
AISIX Enterprise 或 AISIX Cloud
在控制台中打开 Models,编辑模型,然后使用 Rate limits 字段。Admin API 为自动化提供相同配置能力。
更新模型:
curl -sS -X PATCH "${AISIX_CP}/environments/${ENV_ID}/models/${MODEL_ID}" \
-H "Authorization: Bearer ${AISIX_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"rate_limit": {
"rpm": 1
}
}'
开源 AISIX 网关
在直接模型条目中添加限制,并保留已有的模型服务提供方和密钥设置:
models:
- display_name: gpt-4o-prod
provider: openai
model_name: gpt-4o
provider_key: openai-prod
rate_limit:
rpm: 1
验证完整资源文件并重新加载网关。此后,使用不同调用方 API Key 的请求会从同一个模型配额桶中扣减容量。
验证限流
前面的调用方和模型示例都使用每分钟一次请求的限制。请一次只配置并测试一个直接限制,以便 429 响应能明确指向所测试的限制。也可以用相同的请求序列验证限流策略。
等待控制面更改到达网关,或重新加载开源资源文件后,发送三个请求:
for i in 1 2 3; do
printf "request %s: " "${i}"
curl -sS -o /dev/null -w "%{http_code}\n" -X POST "${AISIX_PROXY}/v1/chat/completions" \
-H "Authorization: Bearer ${AISIX_API_KEY}" \
-H "Content-Type: application/json" \
--data-binary @- <<EOF
{
"model": "${AISIX_MODEL}",
"messages": [
{
"role": "user",
"content": "Hello from AISIX."
}
]
}
EOF
done
第一个请求应到达上游模型。同一固定窗口内的其余请求应超过限制:
request 1: 200
request 2: 429
request 3: 429
兼容 OpenAI 的限流拒绝会返回 429 Too Many Requests,并使用代理错误格式:
{
"error": {
"message": "request limit exceeded (requests)",
"type": "rate_limit_exceeded"
}
}
来自调用方 API Key 限制、模型限制或限流策略的拒绝,都会带上 Retry-After,以及 X-RateLimit-Limit、X-RateLimit-Remaining、X-RateLimit-Reset 和 X-RateLimit-Scope,它们描述的是拒绝该请求的那一条限制:
retry-after: 43
x-ratelimit-limit: 1
x-ratelimit-remaining: 0
x-ratelimit-reset: 43
x-ratelimit-scope: rpm
请求计数和 Token 限制的拒绝向其固定窗口的结束时刻倒数。并发拒绝没有窗口——并发槽位在某个在途请求结束时释放——因此固定返回 60 秒的提示。成功的 Chat Completions 响应携带的是另一组按维度拆分的响应头。参见限流拒绝响应头。
限流字段与计数器行为
调用方 API Key 和模型别名使用相同的 rate_limit 字段。每个字段都是可选的;省略某个字段时,AISIX 不会执行相应维度的限制。
| 字段 | 限制 | 固定窗口 |
|---|---|---|
rps | 每秒请求数 | 1 秒 |
rpm | 每分钟请求数 | 60 秒 |
rph | 每小时请求数 | 3,600 秒 |
rpd | 每日请求数 | 86,400 秒 |
tpm | 每分钟 Token 数 | 60 秒 |
tpd | 每日 Token 数 | 86,400 秒 |
concurrency | 进行中的请求数 | 无窗口 |
AISIX 在处理请求的不同时点检查并记录三种限制维度:
| 维度 | AISIX 检查时点 | AISIX 记录用量时点 |
|---|---|---|
| 请求计数 | 调用模型服务提供方之前 | 在调用前检查期间;较早匹 配的层可能已增加计数,随后请求才被较晚的层拒绝 |
| Token | 调用模型服务提供方之前,依据当前窗口中已记录的 Token | 上游响应报告用量之后 |
| 并发 | 调用模型服务提供方之前 | 持有到响应或流结束,然后释放 |
Token 限制包含提示词和生成结果 Token。对于 Anthropic 流量,还包含 Anthropic 与输入 Token 分开报告的提示词缓存创建和缓存读取 Token。OpenAI 缓存 Token 已包含在提示词 Token 中,因此 AISIX 不会重复计数。
由于模型服务提供方在生成响应后才报告用量,跨过 Token 限制的请求可能成功完成。它的 Token 用量可能耗尽当前窗口,导致后续请求被拒绝。
只有上游响应报告的 Token 用量才会增加 Token 计数器。部分媒体调用不报告 Token 用量,因此不会增加 tpm 或 tpd:whisper-1 的转录和翻译、所有文本转语音请求、dall-e-2 和 dall-e-3 的图像生成与编辑,以及视频请求。报告 Token 用量的模型(例如 gpt-4o-transcribe 和 gpt-image-1)照常计数。音频时长仅用于费用计算,不计入 Token 限制。无论调用哪个端点,只要适用的 tpm 或 tpd 窗口已经耗尽,即使该调用不会增加 Token,也仍会被拒绝。
直接关联到调用方 API Key 或模型的 Token 限制只支持分钟和日窗口,这些资源没有每秒或每小时 Token 字段。经典限流策略也会使用分钟和日窗口执行 Token 上限。AISIX Cloud Admin API 会拒绝使用秒或小时窗口的 Token 上限。对于开源 AISIX 网关,资源 Schema 虽接受这些配置,但运行时不会执行相应上限。
模型限制的应用方式
如果请求同时匹配调用方限制和模型限制,AISIX 会在两个配额桶中预留容量。任一配额桶耗尽都会拒绝请求。
当直接模型作为路由模型或语义路由的目标时,模型限制同样适用。路由分发会跳过超出自身限制的目标并继续尝试其余目标;所有目标都超出限制时,请求返回 429。语义分发会执行所选目标的限制;该目标没有容量时返回 429。对同一模型的直接请求和路由请求共享其限制配额桶。
inject 模式的透传路由也可以预留模型限制。当请求正文通过别名或服务提供方原生名称,指定了路由中服务提供方密钥所属服务提供方的某个模型时,AISIX 会预留模型限制。匹配的模型与建模路由共享请求计数和并发限制配额桶;forward_client 路由从不查询模型探测器。调用方层级的请求计数和并发限制适用于每条路由。
适用的 tpm 或 tpd 窗口若已耗尽,仍会拒绝透传请求。但是,即使 AISIX 识别出受支持的信封,并在请求的用量事件中记录了 Token 用量,透传用量也不会增加这些 Token 计数器。
选择限流计数器存储
网关启动配置决定限流计数器是某个进程本地独享,还是由多个网关实例共享。此设置同时适用于直接关联到资源的限制和该网关执行的可复用策略。
| 后端 | 计数器作用域 | 适用场景 |
|---|---|---|
| Memory(默认) | 单个网关进程 | 由单实例执行限制,或可接受每个实例分别计算配额。 |
| Redis | 使用同一 Redis 后端的所有网关实例 | 部署必须执行同一个共享请求数、Token 或并发配额。 |
使用 Memory 后端时,每个网关实例只统计自己处理的流量。因此,在流量均匀分发的多实例部署中,一个窗口内总体放行的流量可能超过所配置的单进程限制。固定路由可以减少同一调用方或租户的这种差异,但 Redis 才是共享计数器方案。
当多个网关实例必须执行同一配额时,请配置 Redis:
ratelimit:
backend: redis
redis:
mode: single
url: redis://127.0.0.1:6379/
当 ratelimit.backend 为 redis 时,网关要求提供 ratelimit.redis 块,缺少该块时启动会失败。启动时连不上 Redis 不会再导致启动失败,但有界的启动连接尝试可能延迟所有监听器。尝试结束后,Redis 不再阻止监听器启动;网关会输出一条 WARN 日志,点明后端、配置的端点值、原因和连接错误。代理监听器可用后,网关按副本计数。该告警每 5 分钟重述一次,同时后台任务会在 Redis 恢复应答后立即接入共享后端。网关不会永久切换到 memory 后端;降级期间不执行集群级限流。
告警会逐字记录配置的端点值:single 模式下的 url、cluster 模式下的 nodes,以及 sentinel 模式下的 sentinels。嵌入这些值中的凭据也会被记录。请使用单独的 username 和 password 字段配置数据节点凭据。Sentinel 节点凭据仍包含在 Sentinel URL 中,因此请限制对网关日志的访问。
无法在后台恢复的本地配置和解析错误会导致启动失败,包括:无法解析的 url;AISIX 无法读取或解析的 TLS 材料;仅配置客户端证书或密钥之一;ratelimit.concurrency_ttl_secs: 0;所选模式缺少 url、nodes、sentinels 或 master_name;timeout_secs: 0;或主机名无法解析到任何地址。Redis 明确应答并拒绝连接配置时走的是降级路径,包括凭据被拒绝、服务端返回 NOAUTH 或 DENIED,以及服务端没有所配置的 database。此时告警带的是 reason=refused 而不是 reason=unreachable,每 5 分钟的重述也带同一个词。网关会持续尝试重新接入,因此在 Redis 一侧修好凭据后无需重启即可采用它。
无论 Redis 中断是在网关启动时就已存在,还是启动之后才发生,限流都会降级为进程本地计数器。请求仍受各进程限制保护,但中断期间部署不会执行统一的集群级配额。中断期间的计数不会复制回 Redis,因此恢复后,在当前活动窗口结束之前,各计数器可能仍不一致。
ratelimit.redis.timeout_secs 决定网关在把请求交给上述失败即放行路径之前会等待多久,默认值为 5 秒,最小值为 1,取 0 会在启动时被拒绝并点名该字段。Redis 恢复应答后,共享计数会自动恢复。
timeout_secs 约束单次 Redis 往返或连接尝试,包括网关启动时的每次连接尝试。超时或连接故障会让该子系统接下来的所有操作在 30 秒内直接短路,而不是再次等待;Redis 服务端返回的错误只会让当前操作失败,不会打开短路。缓存的精确连接和向量连接算作同一个子系统,限流器是另一个。这 30 秒冷却期足以覆盖一次上游调用,因为请求的缓存读取与写入位于调用两侧;因此一个请求为缓存付出一次预算、为限流器付出一次预算,而不是为每个 Redis 操作各付一次。上游环节耗时超过 30 秒的请求仍可能在写入时再付出一份预算。在 cluster 和 sentinel 模式下,网关以 timeout_secs ×(已配置端点数 + 1)作为整个启动拓扑遍历的总预算。该乘积限制整次启动连接,而不是其中每次尝试;每次尝试仍受单个 timeout_secs 限制。single 模式下,整个启动连接只有一个 timeout_secs 预算。
决定冷却期何时结束的是一个后台任务,而不是业务请求:30 秒到期时它向 Redis 发送一次 PING,成功则关闭短路,失败则重新计时 30 秒。在它关闭短路之前,请求会继续直接短路返回,因此没有任何请求需要付出超时代价去探测 Redis 是否仍不可用。Redis 恢复可达后,恢复过程仍可能需要超过一个冷却窗口。
本地并发槽位会在请求完成时释放。SSE 中继会保留每个匹配的槽位,直到响应体关闭或被取消。Redis 支持的中继会在 Redis 可达时续约每个活跃租约。过期槽位在 concurrency_ttl_secs 之后被回收,该值默认为 300 秒。如果 Redis 报告某个活跃租约已不存在,AISIX 会结束透传 SSE 中继。
在混合版本发布期间,应根据旧版副本保持每个流预留的方式处理。如果旧版副本在流的整个生命周期中保留预留,但无法续约,请将 ratelimit.concurrency_ttl_secs 设为长于最长可能预留时间的值,其中包括等待上游响应头和流的生命周期。更长的 TTL 也会延迟回收因副本崩溃而遗留的槽位。
透传 SSE 需要不同的发布方式。旧版副本如果在返回响应头时释放透传预留,则无法靠延长 TTL 来保护。在新版副本开始执行共享限制前,请先停止向这些旧版副本发送新的透传 SSE 请求。旧版副本上已存在的透传流在结束或被终止前,仍不计入该并发数。
Redis 连接模式
ratelimit.redis.mode 字段用于选择 Redis 部署类型,并决定必须提供哪些连接字段。前面的示例使用带单个 url 的 single 模式。
对 Redis Cluster 种子节点使用 cluster:
ratelimit:
backend: redis
redis:
mode: cluster
nodes:
- redis://10.0.0.1:6379/
- redis://10.0.0.2:6379/
对由 Sentinel 管理的主节点使用 sentinel:
ratelimit:
backend: redis
redis:
mode: sentinel
sentinels:
- redis://10.0.0.1:26379/
- redis://10.0.0.2:26379/
master_name: mymaster
各模式必需的字段如下:
| 模式 | 必需连接字段 |
|---|---|
single | url |
cluster | 一个或多个 nodes 条目 |
sentinel | 一个或多个 sentinels 条目及 master_name |
Redis 需要身份认证时,请设置 username 和 password。这两个字段在所有模式下都生效,并且会覆盖内嵌在 url 中的凭据,因此通过环境变量提供的取值不会被 URL 里遗留的旧凭据压过。请把它们作为一对来设置:只设置其中一半会同时替换两半,因为从两个来源拼出的登录信息在任何一处都不存在。database 在 single 和 sentinel 模式下生效;Redis Cluster 只有 DB 0。在 Sentinel 模式中,Sentinel 节点凭证写在 Sentinel URL 中,而 username、password 和 database 应用于发现到的 Redis 主节点。