跳到主要内容

上游限流

Aidy Gateway 支持上游限流能力,用于在请求转发到上游服务前进行流量控制。该功能通过 UpstreamRateLimitPluginConfig 配置,基于 token 数或字符数进行限速。

概述

上游限流插件运行在 ForwardRequest 阶段,在请求即将转发到上游服务时执行限速检查。与入口限速(基于租户 QPS)不同,上游限流支持更灵活的维度控制和基于用量的计费模式。

核心概念

  • 限速桶 (Bucket): 确定限速的维度,决定「谁」被限速
  • 限速条件 (Condition): 触发限速检查的前提条件
  • 限速配额 (Quota): 限速的具体数值和时间窗口

限速规则

一条限速规则由「限速条件」和「限速桶」组成。在满足条件的前提下,查询对应的限速桶进行配额检查。

规则特性

  1. 一一对应: 每条规则的「限速条件」与「限速桶」一一对应,不支持多对多的复合逻辑
  2. 顺序执行: 多条规则自上而下依次执行
  3. 规则上限: 每个路由最多配置 10 条限速规则
  4. 事务补偿: 当同时命中多条规则时,若前面的规则通过(已扣除配额)而后面的规则未通过,会进行事务补偿回滚已扣除的配额
  5. 无兜底规则: 如果不满足任何条件,不做任何限速

隐含桶前缀

每条规则的限速桶会自动添加 route:{RouteID}:rule:{RuleIndex} 作为前缀,确保不同路由、不同规则之间的限速桶隔离。

配置结构

路由配置

在路由的 plugin_config.upstream_rate_limit 中配置限速规则:

plugin_config:
upstream_rate_limit:
rules:
- bucket:
header:
name: "X-Tenant-ID"
conditions:
- header:
name: "X-Model"
starts_with: "gpt-4"
quota:
limit: 100000
window: "3600s" # 1h
unit: RATE_LIMIT_UNIT_TOKENS

限速桶类型

类型说明示例
header基于请求 Header 的值header['X-Tenant-ID'] → 按租户限速
source_addr基于客户端 IP按来源 IP 限速

Header 桶

bucket:
header:
name: "X-Tenant-ID"

客户端 IP 桶

bucket:
source_addr: {}

限速条件

条件用于判断是否触发限速检查。当配置多个条件时,需要全部满足才会触发。条件为空时始终触发。

Header 条件

匹配方式说明示例
equals等于指定值header['X-Model'] = 'gpt-4'
starts_with前缀匹配header['X-Model'] starts with 'gpt-4'
contains包含指定值header['X-Model'] contains 'turbo'
regex正则匹配header['X-Model'] match '^gpt-4.*'
existsHeader 存在header['X-Priority'] exists
conditions:
- header:
name: "X-Model"
starts_with: "gpt-4"
- header:
name: "X-Priority"
exists: true

客户端 IP 条件

匹配方式说明示例
equals等于指定 IPsource_addr = '192.168.1.100'
starts_with前缀匹配source_addr starts with '192.168.1.'
cidrCIDR 网段匹配source_addr in '192.168.0.0/16'
conditions:
- source_addr:
cidr: "10.0.0.0/8"

限速配额

字段类型说明
limitint64限速数量
windowDuration滑动窗口时间(仅支持秒为单位),如 1s60s3600s86400s
unitRateLimitUnit计量单位

计量单位

说明
RATE_LIMIT_UNIT_TOKENS按 token 数计量
RATE_LIMIT_UNIT_CHARACTERS按字符数计量
quota:
limit: 1000000
window: "86400s" # 24h
unit: RATE_LIMIT_UNIT_TOKENS

配置示例

示例 1: 按 IP 每小时 token 限额

对每个客户端 IP 进行每小时 token 限速:

plugin_config:
upstream_rate_limit:
rules:
- bucket:
source_addr: {}
conditions:
- source_addr:
exists: true
quota:
limit: 100000
window: "3600s" # 1h
unit: RATE_LIMIT_UNIT_TOKENS

示例 2: 按自定义用户每小时 token 限速

使用 X-User-ID 请求头标识用户,对每个用户进行每小时 token 限速(仅当 X-User-ID 头存在时触发):

plugin_config:
upstream_rate_limit:
rules:
- bucket:
header:
name: "X-User-ID"
conditions:
- header:
name: "X-User-ID"
exists: true
quota:
limit: 50000
window: "3600s" # 1h
unit: RATE_LIMIT_UNIT_TOKENS

示例 3: 按 IP 限速(针对内网)

仅对来自内网(10.0.0.0/8 网段)的请求进行限速:

plugin_config:
upstream_rate_limit:
rules:
- bucket:
source_addr: {}
conditions:
- source_addr:
cidr: "10.0.0.0/8"
quota:
limit: 500000
window: "60s" # 1min
unit: RATE_LIMIT_UNIT_CHARACTERS

示例 4: 多条规则组合

同时应用多条限速规则,按用户等级实现差异化限速,并叠加整体 IP 限速:

  • 规则 1: 对 VIP 用户(X-User-Level: vip)每小时限 20 万 token
  • 规则 2: 对普通用户(X-User-Level: normal)每小时限 1 万 token
  • 规则 3: 对所有请求按 IP 每分钟限 1 万 token(防止突发流量)
plugin_config:
upstream_rate_limit:
rules:
# 规则 1: VIP 用户每小时 20 万 token
- bucket:
header:
name: "X-User-ID"
conditions:
- header:
name: "X-User-Level"
equals: "vip"
quota:
limit: 200000
window: "3600s" # 1h
unit: RATE_LIMIT_UNIT_TOKENS
# 规则 2: 普通用户每小时 1 万 token
- bucket:
header:
name: "X-User-ID"
conditions:
- header:
name: "X-User-Level"
equals: "normal"
quota:
limit: 10000
window: "3600s" # 1h
unit: RATE_LIMIT_UNIT_TOKENS
# 规则 3: 所有请求按 IP 每分钟 1 万 token(防止突发流量)
- bucket:
source_addr: {}
conditions:
- source_addr:
exists: true
quota:
limit: 10000
window: "60s" # 1min
unit: RATE_LIMIT_UNIT_TOKENS

行为说明

限速触发

当请求触发限速时,插件会返回 429 Too Many Requests 响应,并在响应头中包含限速相关信息。

事务补偿机制

当一个请求匹配多条规则时:

  1. 按顺序检查每条规则的条件
  2. 条件满足时,扣除对应桶的配额
  3. 如果后续规则检查失败(配额不足),会回滚之前所有已扣除的配额
  4. 确保要么所有规则都通过,要么不扣除任何配额

算法:固定窗口

限速采用固定窗口算法,窗口从首次计数时开始计算。例如配置 window: "3600s" 且首次请求发生在 13:23,则窗口为 13:23 ~ 14:23。

备注

目前窗口最低精度为 1s,存在小于 1s 精度的窗口会被向下取整;另外,如果本身窗口就小于 1s,则会被视为无效窗口,不进行限速(后续可能会增加对于这种的检测进行报错)。

预扣(请求阶段)与复扣(响应阶段)

  • 请求进入时,会根据规则的 quota.unit 进行“预扣”:
    • 若单位为 TOKENS,会估算请求 token 数(prompt 侧)。
    • 若单位为 CHARACTERS,会计算请求字符数。
  • 下游完成后,会根据响应体内容(若能解析出 usage 或估算响应)进行“复扣”:
    • TOKENS 单位:
      1. 计算 completion 的实际用量:优先使用 usage.completion_tokens,缺失时回退为基于响应文本的估算;
      2. 若响应中包含 usage.prompt_tokens,将与预扣时的请求估算做“差额调整”:delta = usage.prompt_tokens - 预估请求tokens,复扣时会额外加上该差额(差额可为负数,表示回退预扣阶段的多扣部分);
      3. 最终复扣量 = completion 实际用量 + 差额(若可用)。
    • CHARACTERS 单位:按响应文本字符数计算扣除。
备注
  1. 当前实现没有在下游失败/取消/超时的情况下“回滚”预扣,因此在错误的场景中,配额可能出现偏大计入。
  2. 估算 token 算法目前使用 OpenAI gpt-4o 及更新模型的同款算法(o200k_base),但未来可能会调整。

Redis 异常时的策略(Fail-Open)

当与 Redis 交互发生错误(例如网络异常、脚本执行失败等)时,网关当前策略为“放行”(Fail-Open):

  • 会记录错误日志;
  • 但不会拦截请求。

Redis 集群兼容性

当前版本暂不支持在 Redis Cluster(分片集群)环境下运行该限流功能。

原因:限流通过 Lua 脚本一次性操作多个 Key;在 Cluster 模式下,如这些 Key 不在同一哈希槽(slot),会触发 CROSSSLOT 错误,导致脚本执行失败。

  • 建议:如需生产使用,请采用单实例/主从或哨兵模式的 Redis。
  • 风险提示:若误用 Cluster,脚本可能失败;按当前策略网关将记录错误并 Fail-Open 放行(见上节)。