速率限制
速率限制是一种限制入站请求的策略,指定主机或客户端在特定时间段内发送请求的次数。一旦达到限制(例如每秒 100 个请求),我们就说发出调用的客户端被限速。任何被限速的请求都会被拒绝,永远不会到达上游服务。
速率限制类型
Envoy 支持全局(分布式)和本地(非分布式)两个级别的速率限制。
全局 vs 本地速率限制
- 全局速率限制:控制多个 Envoy 实例之间共享的上游集合的访问
- 本地速率限制:适用于每个 Envoy 实例
全局和本地速率限制可以一起使用,Envoy 分两个阶段应用它们:首先应用本地速率限制,然后应用全局速率限制。
全局速率限制
全局或分布式速率限制在多个主机向少量上游服务器发送请求且平均延迟较低时很有用。
架构
Envoy 与实现定义 RPC/IDL 协议的任何外部速率限制服务集成。参考实现使用 Go、gRPC 和 Redis 作为后端。
配置概念
Actions、Descriptors 和 Descriptors List
在 Envoy 配置中,我们定义一组 actions。每个 action 包含速率限制操作列表。
rate_limits:
- actions:
- header_value_match:
descriptor_value: get_request
headers:
- name: :method
prefix_match: GET
- header_value_match:
descriptor_value: path
headers:
- name: :path
prefix_match: /api
- actions:
- header_value_match:
descriptor_value: post_request
headers:
- name: :method
prefix_match: POST
客户端配置示例
routes:
- match:
prefix: "/users"
route:
cluster: some_cluster
rate_limits:
- actions:
- generic_key:
descriptor_value: users
- header_value_match:
descriptor_value: post_request
headers:
- name: ":method"
exact_match: POST
- actions:
- generic_key:
descriptor_value: users
- match:
prefix: "/api"
route:
cluster: some_cluster
rate_limits:
- actions:
- generic_key:
descriptor_value: api
- request_headers:
header_name: dev
descriptor_key: dev_request
HTTP 过滤器配置
http_filters:
- name: envoy.filters.http.ratelimit
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit
domain: some_domain
enable_x_ratelimit_headers: DRAFT_VERSION_03
rate_limit_service:
transport_api_version: V3
grpc_service:
envoy_grpc:
cluster_name: rate-limit-cluster
服务端配置
domain: some_domain
descriptors:
- key: generic_key
value: users
rate_limit:
unit: MINUTE
requests_per_unit: 20
descriptors:
- key: header_match
value: post_request
rate_limit:
unit: MINUTE
requests_per_unit: 10
- key: generic_key
value: api
descriptors:
- key: dev_request
value: true
rate_limit:
unit: SECOND
requests_per_unit: 10
- key: dev_request
value: false
rate_limit:
unit: SECOND
requests_per_unit: 5
本地速率限制
本地速率限制过滤器对过滤器链处理的入站连接应用令牌桶速率限制。
令牌桶算法
令牌桶算法基于桶中令牌的类比。桶以固定速率重新填充令牌。每次收到请求或连接时,我们检查桶中是否还有令牌。如果有,从桶中移除一个令牌,请求得到处理。如果没有令牌,请求被丢弃(即被限速)。
配置示例
基本配置
token_bucket:
max_tokens: 5000
tokens_per_fill: 100
fill_interval: 30s
filter_enabled:
default_value:
numerator: 100
denominator: HUNDRED
filter_enforced:
default_value:
numerator: 100
denominator: HUNDRED
添加响应头部
token_bucket:
max_tokens: 5000
tokens_per_fill: 100
fill_interval: 30s
filter_enabled:
default_value:
numerator: 100
denominator: HUNDRED
filter_enforced:
default_value:
numerator: 100
denominator: HUNDRED
response_headers_to_add:
- append: false
header:
key: x-local-rate-limit
value: 'true'
全局配置
http_filters:
- name: envoy.filters.http.local_ratelimit
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
stat_prefix: http_local_rate_limiter
token_bucket:
max_tokens: 10000
tokens_per_fill: 1000
fill_interval: 1s
路由级别配置
route_config:
virtual_hosts:
- name: my_service
domains: ["*"]
routes:
- match:
prefix: /
route:
cluster: some_cluster
typed_per_filter_config:
envoy.filters.http.local_ratelimit:
"@type": type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
token_bucket:
max_tokens: 10000
tokens_per_fill: 1000
fill_interval: 1s
filter_enabled:
default_value:
numerator: 100
denominator: HUNDRED
filter_enforced:
default_value:
numerator: 100
denominator: HUNDRED
使用 Descriptors 的本地速率限制
route_config:
virtual_hosts:
- name: my_service
domains: ["*"]
routes:
- match:
prefix: /
route:
cluster: some_cluster
rate_limits:
- actions:
- header_value_match:
descriptor_value: post_request
headers:
- name: ":method"
exact_match: POST
- header_value_match:
descriptor_value: get_request
headers:
- name: ":method"
exact_match: GET
typed_per_filter_config:
envoy.filters.http.local_ratelimit:
"@type": type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
stat_prefix: some_stat_prefix
token_bucket:
max_tokens: 1000
tokens_per_fill: 1000
fill_interval: 60s
filter_enabled:
default_value:
numerator: 100
denominator: HUNDRED
filter_enforced:
default_value:
numerator: 100
denominator: HUNDRED
descriptors:
- entries:
- key: header_match
value: post_request
token_bucket:
max_tokens: 20
tokens_per_fill: 5
fill_interval: 30s
- entries:
- key: header_match
value: get_request
token_bucket:
max_tokens: 50
tokens_per_fill: 5
fill_interval: 20s
速率限制统计
无论使用全局还是本地速率限制,Envoy 都会发出下表描述的指标。我们可以使用 stat_prefix
字段设置统计前缀。
速率限制器 | 指标名称 | 描述 |
---|---|---|
Local | enabled | 调用速率限制器的请求总数 |
Local/Global | ok | 令牌桶的限速内响应总数 |
Local | rate_limited | 没有可用令牌的响应总数(但不一定强制执行) |
Local | enforced | 被限速的请求总数(例如返回 HTTP 429) |
Global | over_limit | 速率限制服务的超限响应总数 |
Global | error | 联系速率限制服务的错误总数 |
Global | failure_mode_allowed | 由于 failure_mode_deny 设置而允许的错误请求总数 |
最佳实践
1. 选择合适的速率限制类型
- 使用本地速率限制进行单实例保护
- 使用全局速率限制进行分布式保护
- 结合使用两种类型
2. 合理设置限制
- 根据服务容量设置限制
- 考虑业务峰值需求
- 定期调整限制参数
3. 监控和告警
- 监控速率限制指标
- 设置适当的告警阈值
- 分析限速模式
注意事项
- 速率限制会影响用户体验
- 需要平衡保护和服务可用性
- 配置变更需要谨慎测试
- 考虑限速的副作用
速率限制是 Envoy 中重要的流量控制功能,合理配置可以有效保护服务免受过载。