RateLimit
rateLimit 中间件确保服务获得合理数量的请求,并允许你定义什么是合理。
它基于令牌桶实现。
在这个类比中,average 和 period 参数定义令牌桶补充令牌的速率,burst 则是令牌桶的大小(容量)。
速率与突发容量¶
速率由 average 除以 period 定义。
要实现低于每秒 1 个请求的速率,请将 period 设为大于 1 秒的值。
配置示例¶
# Here, an average of 100 requests per second is allowed.
# In addition, a burst of 200 requests is allowed.
# Redis distributed rate limiting is configured with all available options.
http:
middlewares:
test-ratelimit:
rateLimit:
average: 100
period: 1s
burst: 200
redis:
endpoints:
- "redis-primary.example.com:6379"
- "redis-replica.example.com:6379"
username: "ratelimit-user"
password: "secure-password"
db: 2
poolSize: 50
minIdleConns: 10
maxActiveConns: 200
readTimeout: 3s
writeTimeout: 3s
dialTimeout: 5s
tls:
ca: "/etc/ssl/redis-ca.crt"
cert: "/etc/ssl/redis-client.crt"
key: "/etc/ssl/redis-client.key"
insecureSkipVerify: false# Here, an average of 100 requests per second is allowed.
# In addition, a burst of 200 requests is allowed.
# Redis distributed rate limiting is configured with all available options.
[http.middlewares]
[http.middlewares.test-ratelimit.rateLimit]
average = 100
period = "1s"
burst = 200
[http.middlewares.test-ratelimit.rateLimit.redis]
endpoints = ["redis-primary.example.com:6379", "redis-replica.example.com:6379"]
username = "ratelimit-user"
password = "secure-password"
db = 2
poolSize = 50
minIdleConns = 10
maxActiveConns = 200
readTimeout = "3s"
writeTimeout = "3s"
dialTimeout = "5s"
[http.middlewares.test-ratelimit.rateLimit.redis.tls]
ca = "/etc/ssl/redis-ca.crt"
cert = "/etc/ssl/redis-client.crt"
key = "/etc/ssl/redis-client.key"
insecureSkipVerify = false# Here, an average of 100 requests per second is allowed.
# In addition, a burst of 200 requests is allowed.
# Redis distributed rate limiting is configured with all available options.
labels:
- "traefik.http.middlewares.test-ratelimit.ratelimit.average=100"
- "traefik.http.middlewares.test-ratelimit.ratelimit.period=1s"
- "traefik.http.middlewares.test-ratelimit.ratelimit.burst=200"
- "traefik.http.middlewares.test-ratelimit.ratelimit.redis.endpoints=redis-primary.example.com:6379,redis-replica.example.com:6379"
- "traefik.http.middlewares.test-ratelimit.ratelimit.redis.username=ratelimit-user"
- "traefik.http.middlewares.test-ratelimit.ratelimit.redis.password=secure-password"
- "traefik.http.middlewares.test-ratelimit.ratelimit.redis.db=2"
- "traefik.http.middlewares.test-ratelimit.ratelimit.redis.poolSize=50"
- "traefik.http.middlewares.test-ratelimit.ratelimit.redis.minIdleConns=10"
- "traefik.http.middlewares.test-ratelimit.ratelimit.redis.maxActiveConns=200"
- "traefik.http.middlewares.test-ratelimit.ratelimit.redis.readTimeout=3s"
- "traefik.http.middlewares.test-ratelimit.ratelimit.redis.writeTimeout=3s"
- "traefik.http.middlewares.test-ratelimit.ratelimit.redis.dialTimeout=5s"
- "traefik.http.middlewares.test-ratelimit.ratelimit.redis.tls.ca=/etc/ssl/redis-ca.crt"
- "traefik.http.middlewares.test-ratelimit.ratelimit.redis.tls.cert=/etc/ssl/redis-client.crt"
- "traefik.http.middlewares.test-ratelimit.ratelimit.redis.tls.key=/etc/ssl/redis-client.key"
- "traefik.http.middlewares.test-ratelimit.ratelimit.redis.tls.insecureSkipVerify=false"// Here, an average of 100 requests per second is allowed.
// In addition, a burst of 200 requests is allowed.
// Redis distributed rate limiting is configured with all available options.
{
"Tags": [
"traefik.http.middlewares.test-ratelimit.ratelimit.average=100",
"traefik.http.middlewares.test-ratelimit.ratelimit.period=1s",
"traefik.http.middlewares.test-ratelimit.ratelimit.burst=200",
"traefik.http.middlewares.test-ratelimit.ratelimit.redis.endpoints=redis-primary.example.com:6379,redis-replica.example.com:6379",
"traefik.http.middlewares.test-ratelimit.ratelimit.redis.username=ratelimit-user",
"traefik.http.middlewares.test-ratelimit.ratelimit.redis.password=secure-password",
"traefik.http.middlewares.test-ratelimit.ratelimit.redis.db=2",
"traefik.http.middlewares.test-ratelimit.ratelimit.redis.poolSize=50",
"traefik.http.middlewares.test-ratelimit.ratelimit.redis.minIdleConns=10",
"traefik.http.middlewares.test-ratelimit.ratelimit.redis.maxActiveConns=200",
"traefik.http.middlewares.test-ratelimit.ratelimit.redis.readTimeout=3s",
"traefik.http.middlewares.test-ratelimit.ratelimit.redis.writeTimeout=3s",
"traefik.http.middlewares.test-ratelimit.ratelimit.redis.dialTimeout=5s",
"traefik.http.middlewares.test-ratelimit.ratelimit.redis.tls.ca=/etc/ssl/redis-ca.crt",
"traefik.http.middlewares.test-ratelimit.ratelimit.redis.tls.cert=/etc/ssl/redis-client.crt",
"traefik.http.middlewares.test-ratelimit.ratelimit.redis.tls.key=/etc/ssl/redis-client.key",
"traefik.http.middlewares.test-ratelimit.ratelimit.redis.tls.insecureSkipVerify=false"
]
}# Here, an average of 100 requests per second is allowed.
# In addition, a burst of 200 requests is allowed.
# Redis distributed rate limiting is configured with all available options.
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: test-ratelimit
spec:
rateLimit:
average: 100
period: 1s
burst: 200
redis:
endpoints:
- "redis-primary.example.com:6379"
- "redis-replica.example.com:6379"
secret: redis-credentials
db: 2
poolSize: 50
minIdleConns: 10
maxActiveConns: 200
readTimeout: 3s
writeTimeout: 3s
dialTimeout: 5s
tls:
caSecret: redis-ca
certSecret: redis-client-cert
insecureSkipVerify: false
---
apiVersion: v1
kind: Secret
metadata:
name: redis-credentials
namespace: default
data:
username: cmF0ZWxpbWl0LXVzZXI= # base64 encoded "ratelimit-user"
password: c2VjdXJlLXBhc3N3b3Jk # base64 encoded "secure-password"
---
apiVersion: v1
kind: Secret
metadata:
name: redis-ca
namespace: default
data:
tls.ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t...
---
apiVersion: v1
kind: Secret
metadata:
name: redis-client-cert
namespace: default
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t...
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0t...配置选项¶
| 字段 | 说明 | 默认值 | 必填 |
|---|---|---|---|
average |
配合 period 定义速率的请求数。0 表示不限流。 更多信息见此处。 |
0 | 否 |
period |
用于定义速率的时间周期。 更多信息见此处。 |
1s | 否 |
burst |
同一时刻允许通过的最大请求数。 更多信息见此处。 |
1 | 否 |
sourceCriterion.requestHost |
是否将请求主机作为来源。sourceCriterion 的更多信息见此处。 |
false | 否 |
sourceCriterion.requestHeaderName |
用于对传入请求进行分组的请求头名称。sourceCriterion 的更多信息见此处。 |
"" | 否 |
sourceCriterion.ipStrategy.depth |
从右侧开始,在 X-Forwarded-For 头中选择 IP 的深度位置。0 表示不使用深度。 如果大于 X-Forwarded-For 中的 IP 总数,客户端 IP 为空。如果大于 0,则不计算 excludedIPs 选项。更多信息参阅下文的 sourceCriterion、ipStrategy 和 depth。 |
0 | 否 |
sourceCriterion.ipStrategy.excludedIPs |
允许扫描 X-Forwarded-For 头,选取第一个不在此列表中的 IP。如果指定了 depth,则忽略 excludedIPs。更多信息参阅下文的 sourceCriterion、ipStrategy 和 excludedIPs。 |
否 | |
sourceCriterion.ipStrategy.ipv6Subnet |
如果提供 ipv6Subnet,且选中的 IP 为 IPv6,则将该 IP 转换为其所属子网的第一个 IP。更多信息参阅下文的 sourceCriterion 和 ipStrategy.ipv6Subnet。 |
否 | |
redis |
redis 配置通过 Redis 在多个 Traefik 实例之间存储限流令牌,启用分布式限流。这可以在 Traefik 代理集群中实施一致的限流。未配置 Redis 时,Traefik 使用内存存储进行限流,仅对单个 Traefik 实例生效。 |
否 | |
redis.endpoints |
用于分布式限流的 Redis 服务器端点列表。可以指定多个端点,以适应 Redis 集群或高可用配置。 | "localhost:6379" | 否 |
redis.username |
Redis 认证使用的用户名。 | "" | 否 |
redis.password |
Redis 认证使用的密码。在 Kubernetes 中,可以通过 Secret 提供。 | "" | 否 |
redis.db |
要选择的 Redis 数据库编号。 | 0 | 否 |
redis.poolSize |
定义连接池中套接字连接的基础数量。设为 0 时,默认为 runtime.GOMAXPROCS 报告的每个 CPU 核心 10 个连接。如果池中连接不足,会分配超出 poolSize 的新连接,最多达到 maxActiveConns。 |
0 | 否 |
redis.minIdleConns |
连接池中要保持的最小空闲连接数。新建连接较慢时,此选项很有用。值为 0 表示不会自动关闭空闲连接。 | 0 | 否 |
redis.maxActiveConns |
连接池在任意时刻可以分配的最大连接数。值为 0 表示不限制。 | 0 | 否 |
redis.readTimeout |
套接字读取超时。达到此时间时,命令以超时失败,而非继续阻塞。零表示无超时。 | 3s | 否 |
redis.writeTimeout |
套接字写入超时。达到此时间时,命令以超时失败,而非继续阻塞。零表示无超时。 | 3s | 否 |
redis.dialTimeout |
建立新连接的超时。零表示无超时。 | 5s | 否 |
redis.tls.ca |
用于与 Redis 建立安全连接的证书颁发机构证书路径,默认使用系统证书包。 | "" | 否 |
redis.tls.cert |
用于与 Redis 建立安全连接的公钥证书路径。设置此选项时,必须设置 key 选项。 |
"" | 否 |
redis.tls.key |
用于与 Redis 建立安全连接的私钥路径。设置此选项时,必须设置 cert 选项。 |
"" | 否 |
redis.tls.insecureSkipVerify |
如果 insecureSkipVerify 为 true,到 Redis 的 TLS 连接将接受服务端提供的任何证书,而不考虑其涵盖的主机名。 |
false | 否 |
sourceCriterion 来源判定标准¶
sourceCriterion 选项定义将请求归为同一来源时使用的判定标准。
如果同时定义多种策略,将报错。
如果均未设置,默认使用请求的远程地址字段(作为 ipStrategy)。
ipStrategy IP 策略¶
ipStrategy 选项定义 depth、excludedIPs 和 ipv6Subnet 三个参数,用于配置 Traefik 如何确定客户端 IP。
限流作为中间件,在实际向后端代理请求之前执行。
此外,前一网络跳点仅在代理过程的最后阶段才会追加到 X-Forwarded-For 中,此时请求已经过限流。
因此,在限流期间,前一网络跳点尚未出现在 X-Forwarded-For 中,无法找到它,也不能依赖它。
sourceCriterion.ipStrategy.ipv6Subnet IPv6 子网¶
此策略仅适用于 Depth 和 RemoteAddr 策略。
如果提供 ipv6Subnet,且选中的 IP 为 IPv6,则将该 IP 转换为其所属子网的第一个 IP。
这可用于将 IPv6 地址按子网分组,防止通过获取新的 IPv6 地址来绕过此中间件。
- 如果
ipv6Subnet的值不在 0–128 范围内,则忽略此项。
ipv6Subnet 示例¶
提供 ipv6Subnet 时,IP 按以下方式转换。
IP |
ipv6Subnet |
clientIP(客户端 IP) |
|---|---|---|
"::abcd:1111:2222:3333" |
64 |
"::0:0:0:0" |
"::abcd:1111:2222:3333" |
80 |
"::abcd:0:0:0:0" |
"::abcd:1111:2222:3333" |
96 |
"::abcd:1111:0:0:0" |
sourceCriterion.ipStrategy.depth 深度¶
如果 depth 设置为 2,且请求的 X-Forwarded-For 头为 "10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1",则“真实”客户端 IP 为 "10.0.0.1"(深度为 4),但用作判定标准的 IP 是 "12.0.0.1"(depth=2)。
X-Forwarded-For |
depth |
clientIP(客户端 IP) |
|---|---|---|
"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1" |
1 |
"13.0.0.1" |
"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1" |
3 |
"11.0.0.1" |
"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1" |
5 |
"" |
sourceCriterion.ipStrategy.excludedIPs 排除的 IP¶
尽管名称可能引起误解,此选项并不是将某个 IP 排除在限流器之外,因此不能用来停用某些 IP 的限流。
excludedIPs 用于解决两类略有不同的使用场景:
- 区分位于同一组反向代理后面的 IP,使它们各自独立计入自己的限流“桶”(参见令牌桶)。
在这种情况下,应将
excludedIPs设置为要排除的X-Forwarded-For IPs列表,从而找到真正的客户端 IP。
将每个 IP 用作独立来源的示例:
X-Forwarded-For |
excludedIPs | clientIP(客户端 IP) |
|---|---|---|
"10.0.0.1,11.0.0.1,12.0.0.1" |
"11.0.0.1,12.0.0.1" |
"10.0.0.1" |
"10.0.0.2,11.0.0.1,12.0.0.1" |
"11.0.0.1,12.0.0.1" |
"10.0.0.2" |
- 将一组 IP(同样位于共同的一组反向代理之后)归为一组,使其被视为同一来源,并全部计入同一个限流桶。
将多个 IP 归为同一来源的示例:
X-Forwarded-For |
excludedIPs | clientIP(客户端 IP) |
|---|---|---|
"10.0.0.1,11.0.0.1,12.0.0.1" |
"12.0.0.1" |
"11.0.0.1" |
"10.0.0.2,11.0.0.1,12.0.0.1" |
"12.0.0.1" |
"11.0.0.1" |
"10.0.0.3,11.0.0.1,12.0.0.1" |
"12.0.0.1" |
"11.0.0.1" |