跳转到内容

AI Token 限流

功能说明

ai-token-ratelimit 插件实现了基于特定键值实现 token 限流,键值来源可以是 URL 参数、HTTP 请求头、客户端 IP 地址、consumer 名称、cookie 中 key 名称。

配置说明

配置项数据类型必填默认值说明
rule_namestring-限流规则名称,根据限流规则名称 + 限流类型 + 限流 key 名称 + 限流 key 对应的实际值来拼装 redis key
rule_itemsarray of object-限流规则项,按照 rule_items 下的排列顺序,匹配第一个 rule_item 后命中限流规则,后续规则将被忽略
rejected_codeint429请求被限流时,返回的 HTTP 状态码
rejected_msgstringToo many requests请求被限流时,返回的响应体
redisobject-redis相关配置

rule_items 中每一项的配置字段说明。

配置项数据类型必填默认值说明
limit_by_headerstring否,limit_by_* 中选填一项-配置获取限流键值的来源 HTTP 请求头名称
limit_by_paramstring否,limit_by_* 中选填一项-配置获取限流键值的来源 URL 参数名称
limit_by_consumerstring否,limit_by_* 中选填一项-根据 consumer 名称进行限流,无需添加实际值
limit_by_cookiestring否,limit_by_* 中选填一项-配置获取限流键值的来源 Cookie中 key 名称
limit_by_per_headerstring否,limit_by_* 中选填一项-按规则匹配特定 HTTP 请求头,并对每个请求头分别计算限流,配置获取限流键值的来源 HTTP 请求头名称,配置 limit_keys 时支持正则表达式或 *
limit_by_per_paramstring否,limit_by_* 中选填一项-按规则匹配特定 URL 参数,并对每个参数分别计算限流,配置获取限流键值的来源 URL 参数名称,配置 limit_keys 时支持正则表达式或 *
limit_by_per_consumerstring否,limit_by_* 中选填一项-按规则匹配特定 consumer,并对每个 consumer 分别计算限流,根据 consumer 名称进行限流,无需添加实际值,配置limit_keys时支持正则表达式或*
limit_by_per_cookiestring否,limit_by_* 中选填一项-按规则匹配特定 Cookie,并对每个 Cookie 分别计算限流,配置获取限流键值的来源 Cookie 中 key 名称,配置 limit_keys 时支持正则表达式或*
limit_by_per_ipstring否,limit_by_* 中选填一项-按规则匹配特定 IP,并对每个 IP 分别计算限流,配置获取限流键值的来源 IP 参数名称,从请求头获取,以 from-header-对应的header名,示例:from-header-x-forwarded-for,直接获取对端 socket ip,配置为 from-remote-addr
limit_keysarray of object-配置匹配键值后的限流次数

limit_keys 中每一项的配置字段说明。

配置项数据类型必填默认值说明
keystring-匹配的键值,limit_by_per_header,limit_by_per_param,limit_by_per_consumer,limit_by_per_cookie 类型支持配置正则表达式(以 regexp: 开头后面跟正则表达式)或者 *(代表所有),正则表达式示例:regexp:^d.*(以d开头的所有字符串);limit_by_per_ip 支持配置 IP 地址或 IP 段
token_per_secondint否,token_per_second,token_per_minute,token_per_hour,token_per_day 中选填一项-允许每秒请求 token 数
token_per_minuteint否,token_per_second,token_per_minute,token_per_hour,token_per_day 中选填一项-允许每分钟请求 token 数
token_per_hourint否,token_per_second,token_per_minute,token_per_hour,token_per_day 中选填一项-允许每小时请求 token 数
token_per_dayint否,token_per_second,token_per_minute,token_per_hour,token_per_day 中选填一项-允许每天请求 token 数

redis 中每一项的配置字段说明。

配置项数据类型必填默认值说明
service_namestring必填-redis 服务名称,带服务类型的完整 FQDN 名称,例如 my-redis.dns、redis.my-ns.svc.cluster.local
service_portint服务类型为固定地址(static service)默认值为 80,其他为 6379输入 redis 服务的服务端口
usernamestring-redis 用户名
passwordstring-redis 密码
timeoutint1000redis 连接超时时间,单位毫秒

配置示例

识别请求参数 apikey,进行区别限流

rule_name: default_rule
rule_items:
- limit_by_param: apikey
limit_keys:
- key: 9a342114-ba8a-11ec-b1bf-00163e1250b5
token_per_minute: 10
- key: a6a6d7f2-ba8a-11ec-bec2-00163e1250b5
token_per_hour: 100
- limit_by_per_param: apikey
limit_keys:
# 正则表达式,匹配以 a 开头的所有字符串,每个 apikey 对应的请求 10qds
- key: "regexp:^a.*"
token_per_second: 10
# 正则表达式,匹配以 b 开头的所有字符串,每个 apikey 对应的请求 100qd
- key: "regexp:^b.*"
token_per_minute: 100
# 兜底用,匹配所有请求,每个 apikey 对应的请求 1000qdh
- key: "*"
token_per_hour: 1000
redis:
service_name: redis.static

识别请求头 x-ca-key,进行区别限流

rule_name: default_rule
rule_items:
- limit_by_header: x-ca-key
limit_keys:
- key: 102234
token_per_minute: 10
- key: 308239
token_per_hour: 10
- limit_by_per_header: x-ca-key
limit_keys:
# 正则表达式,匹配以 a 开头的所有字符串,每个 apikey 对应的请求 10qds
- key: "regexp:^a.*"
token_per_second: 10
# 正则表达式,匹配以 b 开头的所有字符串,每个 apikey 对应的请求 100qd
- key: "regexp:^b.*"
token_per_minute: 100
# 兜底用,匹配所有请求,每个 apikey 对应的请求 1000qdh
- key: "*"
token_per_hour: 1000
redis:
service_name: redis.static

根据请求头 x-forwarded-for 获取对端 IP,进行区别限流

rule_name: default_rule
rule_items:
- limit_by_per_ip: from-header-x-forwarded-for
limit_keys:
# 精确 ip
- key: 1.1.1.1
token_per_day: 10
# ip 段,符合这个 ip 段的 ip,每个 ip 100qpd
- key: 1.1.1.0/24
token_per_day: 100
# 兜底用,即默认每个 ip 1000qpd
- key: 0.0.0.0/0
token_per_day: 1000
redis:
service_name: redis.static

识别 consumer,进行区别限流

rule_name: default_rule
rule_items:
- limit_by_consumer: ''
limit_keys:
- key: consumer1
token_per_second: 10
- key: consumer2
token_per_hour: 100
- limit_by_per_consumer: ''
limit_keys:
# 正则表达式,匹配以 a 开头的所有字符串,每个 consumer 对应的请求 10qds
- key: "regexp:^a.*"
token_per_second: 10
# 正则表达式,匹配以 b 开头的所有字符串,每个 consumer 对应的请求 100qd
- key: "regexp:^b.*"
token_per_minute: 100
# 兜底用,匹配所有请求,每个 consumer 对应的请求 1000qdh
- key: "*"
token_per_hour: 1000
redis:
service_name: redis.static
rule_name: default_rule
rule_items:
- limit_by_cookie: key1
limit_keys:
- key: value1
token_per_minute: 10
- key: value2
token_per_hour: 100
- limit_by_per_cookie: key1
limit_keys:
# 正则表达式,匹配以 a 开头的所有字符串,每个 cookie 中的 value 对应的请求 10qds
- key: "regexp:^a.*"
token_per_second: 10
# 正则表达式,匹配以 b 开头的所有字符串,每个 cookie 中的 value 对应的请求 100qd
- key: "regexp:^b.*"
token_per_minute: 100
# 兜底用,匹配所有请求,每个 cookie 中的 value 对应的请求 1000qdh
- key: "*"
token_per_hour: 1000
rejected_code: 200
rejected_msg: '{"code":-1,"msg":"Too many requests"}'
redis:
service_name: redis.static

完整示例

AI Token 限流插件依赖 Redis 记录剩余可用的 token 数,因此首先需要部署 Redis 服务。

apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
labels:
app: redis
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis
ports:
- containerPort: 6379
---
apiVersion: v1
kind: Service
metadata:
name: redis
labels:
app: redis
spec:
ports:
- port: 6379
targetPort: 6379
selector:
app: redis
---

在本例中,使用通义千问作为 AI 服务提供商。另外还需要设置 AI 统计插件,因为 AI Token 限流插件依赖 AI 统计插件计算每次请求消耗的 token 数,以下配置限制每分钟的 input 和 output token 总数为 200 个。

apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
name: ai-proxy
namespace: higress-system
spec:
matchRules:
- config:
provider:
type: qwen
apiTokens:
- "<YOUR_API_TOKEN>"
modelMapping:
'gpt-3': "qwen-turbo"
'gpt-35-turbo': "qwen-plus"
'gpt-4-turbo': "qwen-max"
'*': "qwen-turbo"
ingress:
- qwen
url: oci://higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/wasm-go-ai-proxy:v1.0.0
phase: UNSPECIFIED_PHASE
priority: 100
---
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
name: ai-statistics
namespace: higress-system
spec:
defaultConfig:
enable: true
url: oci://higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/wasm-go-ai-token-statistics:v1.0.0
phase: UNSPECIFIED_PHASE
priority: 200
---
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
name: ai-token-ratelimit
namespace: higress-system
spec:
defaultConfig:
rule_name: default_limit_by_param_apikey
rule_items:
- limit_by_param: apikey
limit_keys:
- key: 123456
token_per_minute: 200
redis:
# 默认情况下,为了减轻数据面的压力,Higress 的 global.onlyPushRouteCluster 配置参数被设置为 true,意味着不会自动发现 Kubernetes Service
# 如果需要使用 Kubernetes Service 作为服务发现,可以将 global.onlyPushRouteCluster 参数设置为 false,
# 这样就可以直接将 service_name 设置为 Kubernetes Service, 而无须为 Redis 创建 McpBridge 以及 Ingress 路由
# service_name: redis.default.svc.cluster.local
service_name: redis.dns
service_port: 6379
url: oci://higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/wasm-go-ai-token-ratelimit:v1.0.0
phase: UNSPECIFIED_PHASE
priority: 600

注意,AI Token 限流插件中的 Redis 配置项 service_name 来自 McpBridge 中配置的服务来源,另外我们还需要在 McpBridge 中配置通义千问服务的访问地址。

apiVersion: networking.higress.io/v1
kind: McpBridge
metadata:
name: default
namespace: higress-system
spec:
registries:
- domain: dashscope.aliyuncs.com
name: qwen
port: 443
type: dns
- domain: redis.default.svc.cluster.local # Kubernetes Service
name: redis
type: dns
port: 6379

分别创建两条路由规则。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
higress.io/backend-protocol: HTTPS
higress.io/destination: qwen.dns
higress.io/proxy-ssl-name: dashscope.aliyuncs.com
higress.io/proxy-ssl-server-name: "on"
labels:
higress.io/resource-definer: higress
name: qwen
namespace: higress-system
spec:
ingressClassName: higress
rules:
- host: qwen-test.com
http:
paths:
- backend:
resource:
apiGroup: networking.higress.io
kind: McpBridge
name: default
path: /
pathType: Prefix
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
higress.io/destination: redis.dns
higress.io/ignore-path-case: "false"
labels:
higress.io/resource-definer: higress
name: redis
spec:
ingressClassName: higress
rules:
- http:
paths:
- backend:
resource:
apiGroup: networking.higress.io
kind: McpBridge
name: default
path: /
pathType: Prefix

触发限流效果如下:

Terminal window
curl "http://qwen-test.com:18000/v1/chat/completions?apikey=123456" -H "Content-Type: application/json" -d '{
"model": "gpt-3",
"messages": [
{
"role": "user",
"content": "你好,你是谁?"
}
],
"stream": false
}'
{"id":"88cfa80f-545d-93b4-8ff3-3f5245ca33ba","choices":[{"index":0,"message":{"role":"assistant","content":"我是通义千问,由阿里云开发的AI助手。我可以回答各种问题、提供信息和与用户进行对话。有什么我可以帮助你的吗?"},"finish_reason":"stop"}],"created":1719909825,"model":"qwen-turbo","object":"chat.completion","usage":{"prompt_tokens":13,"completion_tokens":33,"total_tokens":46}}
curl "http://qwen-test.com:18000/v1/chat/completions?apikey=123456" -H "Content-Type: application/json" -d '{
"model": "gpt-3",
"messages": [
{
"role": "user",
"content": "你好,你是谁?"
}
],
"stream": false
}'
Too many requests # 限流成功