跳到主要内容

limit-count-advanced

limit-count-advanced 插件使用固定窗口或滑动窗口算法,通过限制给定时间间隔内的请求数量来限制请求速率。超过配置配额的请求将被拒绝。

具体来说:

  • 固定窗口算法在不重叠的时间间隔内跟踪请求。如果请求计数在任何间隔内超过配额,多余的请求将立即被拒绝,直到下一个时间窗口开始。
  • 滑动窗口算法在重叠的间隔内跟踪请求,通过计算过去配置的时间段内的最近请求来平滑速率限制,而不管间隔何时开始。此方法减少了流量峰值,并且更有效地在一段时间内均匀分布请求。

此外,你可能还会看到以下速率限制响应头,其名称可以使用插件元数据进行自定义:

  • X-RateLimit-Limit:总配额
  • X-RateLimit-Remaining:剩余配额
  • X-RateLimit-Reset:计数器重置前的剩余秒数

偶尔,你可能会观察到 X-RateLimit-Remaining 出现较小的负值。这是可以接受的,因为滑动窗口算法是一种近似值。

示例

除了 limit-count 插件功能外,该插件还支持滑动窗口算法。请参考 limit-count 插件以获取固定窗口示例,这些示例也可以在 limit-count-advanced 中配置。

以下示例演示了如何使用 limit-count-advanced 进行滑动窗口算法的速率限制。

使用本地计数器进行速率限制

以下示例演示了如何配置 limit-count-advanced 在路由上使用滑动窗口算法进行速率限制,并使用网关中的计数器。请注意,每个网关实例都有自己的计数器和独立配额。如果你有多个网关实例需要共享相同的配额,请参阅使用 Redis 服务器在网关之间共享配额

创建一个启用了 limit-count-advanced 插件的路由,配置为每个远程地址在 10 秒滑动窗口内允许的配额为 5:

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "limit-count-sliding-route",
"uri": "/get",
"plugins": {
"limit-count-advanced": {
"policy": "local",
"count": 5,
"time_window": 10,
"rejected_code": 429,
"key_type": "var",
"key": "remote_addr",
"window_type": "sliding"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'

每隔一秒生成 7 个请求到该路由:

for i in $(seq 7); do
(curl -I "http://127.0.0.1:9080/get" &)
sleep 1
done

你应该收到大多数请求的 HTTP/1.1 200 OK 响应,其余为 HTTP 429 Too Many Requests 响应。具体被拒绝的数量取决于第一个请求发送的时间。

使用 Redis 服务器在网关之间共享配额

以下示例演示了如何使用 Redis 服务器在多个网关节点之间使用滑动窗口算法进行速率限制,从而使不同的网关节点共享相同的速率限制配额。

在网关组中创建一个具有以下配置的路由:

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "limit-count-sliding-route",
"uri": "/get",
"plugins": {
"limit-count-advanced": {
"count": 1,
"time_window": 30,
"rejected_code": 429,
"key": "remote_addr",
"policy": "redis",
"redis_host": "192.168.xxx.xxx",
"redis_port": 6379,
"redis_password": "p@ssw0rd",
"redis_database": 1,
"window_type": "sliding",
"sync_interval": 0.2
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'

policy:设置为 redis 以使用 Redis 实例进行速率限制。

redis_host:设置为 Redis 实例的 IP 地址。

redis_port:设置为 Redis 实例的监听端口。

redis_password:如果有,设置为 Redis 实例的密码。

redis_database:设置为 Redis 实例中的数据库编号。

window_type:将窗口类型设置为滑动窗口。

sync_interval:设置同步间隔(可选)。

每隔一秒生成 7 个请求到该路由:

for i in $(seq 7); do
(curl -I "http://127.0.0.1:9080/get" &)
sleep 1
done

你应该收到大多数请求的 HTTP/1.1 200 OK 响应,其余为 HTTP 429 Too Many Requests 响应。具体被拒绝的数量取决于第一个请求发送的时间。这验证了配置在不同网关节点上的路由共享相同的配额。

使用 Redis 集群在网关节点之间共享配额

以下示例演示了如何配置 limit-count-advanced 使用滑动窗口算法,并在多个网关节点之间应用相同的配额,从而使不同的网关节点共享相同的速率限制配额。

确保你的 Redis 实例运行在集群模式(Cluster Mode)limit-count-advanced 插件配置至少需要两个节点。

在网关组中创建一个具有以下配置的路由:

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "limit-count-route",
"uri": "/get",
"plugins": {
"limit-count-advanced": {
"count": 1,
"time_window": 30,
"rejected_code": 429,
"key": "remote_addr",
"policy": "redis-cluster",
"redis_cluster_nodes": [
"192.168.xxx.xxx:6379",
"192.168.xxx.xxx:16379"
],
"redis_password": "p@ssw0rd",
"redis_cluster_name": "redis-cluster-1",
"redis_cluster_ssl": true,
"window_type": "sliding",
"sync_interval": 0.2
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'

policy:设置为 redis-cluster 以使用 Redis 集群进行速率限制。

redis_cluster_nodes:设置为 Redis 集群中的 Redis 节点地址。

redis_password:如果有,设置为 Redis 集群的密码。

redis_cluster_name:设置为 Redis 集群名称。

redis_cluster_ssl:启用与 Redis 集群的 SSL/TLS 通信。

window_type:将窗口类型设置为滑动窗口。

sync_interval:设置同步间隔(可选)。

每隔一秒生成 7 个请求到该路由:

for i in $(seq 7); do
(curl -I "http://127.0.0.1:9080/get" &)
sleep 1
done

你应该收到大多数请求的 HTTP/1.1 200 OK 响应,其余为 HTTP 429 Too Many Requests 响应。具体被拒绝的数量取决于第一个请求发送的时间。这验证了配置在不同网关节点上的路由共享相同的配额。

自定义速率限制头

以下示例演示了如何使用插件元数据自定义速率限制响应头名称,默认情况下为 X-RateLimit-LimitX-RateLimit-RemainingX-RateLimit-Reset

配置此插件的插件元数据并更新头部:

curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/limit-count" -X PUT -d '
{
"log_format": {
"limit_header": "X-Custom-RateLimit-Limit",
"remaining_header": "X-Custom-RateLimit-Remaining",
"reset_header": "X-Custom-RateLimit-Reset"
}
}'

创建一个启用了 limit-count-advanced 插件的路由,配置为每个远程地址在 30 秒窗口内允许的配额为 1:

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "limit-count-advanced-route",
"uri": "/get",
"plugins": {
"limit-count-advanced": {
"policy": "local",
"count": 1,
"time_window": 30,
"rejected_code": 429,
"key_type": "var",
"key": "remote_addr",
"window_type": "sliding"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'

发送请求进行验证:

curl -i "http://127.0.0.1:9080/get"

你应该收到一个 HTTP/1.1 200 OK 响应并看到以下头部:

X-Custom-RateLimit-Limit: 1
X-Custom-RateLimit-Remaining: 0
X-Custom-RateLimit-Reset: 28

使用 Redis Sentinel 在网关节点之间共享配额

以下示例演示了如何使用带有 Redis Sentinel 策略的 limit-count-advanced 插件进行速率限制。

确保你的 Redis 实例运行在 Sentinel 模式

在网关组中创建一个具有以下配置的路由:

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "limit-count-route",
"uri": "/get",
"plugins": {
"limit-count-advanced": {
"count": 1,
"time_window": 30,
"rejected_code": 429,
"key": "remote_addr",
"policy": "redis-sentinel",
"redis_sentinels": [
{"host": "127.0.0.1", "port": 26379},
{"host": "127.0.10.1", "port": 26379},
{"host": "127.0.101.1", "port": 26379}
],
"redis_master_name": "mymaster",
"redis_role": "master",
"sentinel_username": "admin",
"sentinel_password": "admin-password"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'

policy:设置为 redis-sentinel 以使用 Sentinel 模式下的 Redis 进行速率限制。

redis_sentinels:配置 Sentinel 节点地址列表(主机和端口)。

redis_master_name:配置 Sentinel 监控的 Redis 主组名称。

redis_role:设置为 master 以连接到当前的 Redis 主节点。

sentinel_username:配置用于通过 Redis Sentinel 进行身份验证的用户名。

sentinel_password:配置用于通过 Redis Sentinel 进行身份验证的密码。

每隔一秒生成 5 个请求到该路由:

for i in $(seq 5); do
(curl -I "http://127.0.0.1:9080/get" &)
sleep 1
done

你应该在 30 秒窗口内收到一个请求的 HTTP/1.1 200 OK 响应,其余为 HTTP 429 Too Many Requests 响应。

基于规则的速率限制

以下示例演示了如何配置 limit-count-advanced 以根据请求属性应用不同的速率限制规则(从 API7 Enterprise 3.8.17 开始可用)。在此示例中,速率限制基于代表调用者访问层级的 HTTP 头值应用。

请注意,所有规则按顺序应用。如果配置的键不存在,则将跳过相应的规则。

提示

除了 HTTP 头之外,你还可以根据其他内置变量来制定规则,以实现更灵活和细粒度的速率限制策略。

创建一个启用了 limit-count-advanced 插件的路由,该插件根据请求头应用不同的速率限制,允许每个订阅(X-Subscription-ID)进行速率限制,并对试用用户(X-Trial-ID)强制执行更严格的限制:

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "limit-count-rules-route",
"uri": "/get",
"plugins": {
"limit-count-advanced": {
"policy": "local",
"rejected_code": 429,
"rules": [
{
"key": "${http_x_subscription_id}",
"count": "${http_x_custom_count ?? 5}",
"time_window": 60
},
{
"key": "${http_x_trial_id}",
"count": 1,
"time_window": 60
}
]
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'

❶ 使用 X-Subscription-ID 请求头的值作为速率限制键。

❷ 根据 X-Custom-Count 头动态设置请求限制。如果未提供该头,则应用默认计数 5 个请求。

❸ 使用 X-Trial-ID 请求头的值作为速率限制键。

要验证速率限制,使用相同的订阅 ID 生成 7 个请求到该路由:

resp=$(seq 7 | xargs -I{} curl "http://127.0.0.1:9080/get" -H "X-Subscription-ID: sub-123456789" -o /dev/null -s -w "%{http_code}\n") && \
count_200=$(echo "$resp" | grep "200" | wc -l) && \
count_429=$(echo "$resp" | grep "429" | wc -l) && \
echo "200": $count_200, "429": $count_429

你应该会看到以下响应,显示当未提供 X-Custom-Count 头时,应用了 5 个请求的默认计数:

200:        5, 429:        2

等待时间窗口重置。使用相同的订阅 ID 生成 5 个请求到该路由,并将 X-Custom-Count 头设置为 3:

resp=$(seq 5 | xargs -I{} curl "http://127.0.0.1:9080/get" -H "X-Subscription-ID: sub-123456789" -H "X-Custom-Count: 3" -o /dev/null -s -w "%{http_code}\n") && \
count_200=$(echo "$resp" | grep "200" | wc -l) && \
count_429=$(echo "$resp" | grep "429" | wc -l) && \
echo "200": $count_200, "429": $count_429

你应该会看到以下响应,显示应用了来自 X-Custom-Count 头的 3 个请求的计数:

200:        3, 429:        2

最后,使用相同的试用 ID 生成 3 个请求到该路由:

resp=$(seq 3 | xargs -I{} curl "http://127.0.0.1:9080/get" -H "X-Trial-ID: trial-123456789" -o /dev/null -s -w "%{http_code}\n") && \
count_200=$(echo "$resp" | grep "200" | wc -l) && \
count_429=$(echo "$resp" | grep "429" | wc -l) && \
echo "200": $count_200, "429": $count_429

你应该会看到以下响应,显示应用了来自第二条规则的 1 个请求的计数:

200:        1, 429:        2