技术深度解析
envoyproxy/ratelimit 的核心是一个实现了 Envoy 速率限制服务 (RLS) API 的 gRPC 服务器。其架构看似简单,但非常高效。从请求处理的角度来看,该服务是无状态的;所有关于速率限制计数器的状态都存储在 Redis 集群中。这一设计选择至关重要:它允许速率限制服务无需复杂的状态同步即可水平扩展,同时 Redis 提供了精确计数所需的原子操作。
架构流程:
1. Envoy 拦截传入请求并提取描述符(例如,`{descriptor_key: "user_id", value: "123"}`)。
2. Envoy 向速率限制服务发送一个 gRPC `ShouldRateLimit` 请求,其中包含这些描述符。
3. 速率限制服务查找与描述符匹配的已配置规则(从 YAML 文件或动态源加载)。
4. 对于每条匹配的规则,它使用原子操作(滑动窗口使用 INCR 和 EXPIRE,令牌桶使用 Lua 脚本)在 Redis 中递增计数器。
5. 它返回一个响应,指示请求是否应被限速,如果是,则返回要发送的标头(例如,`Retry-After`)。
算法详解:
- 令牌桶: 使用 Lua 脚本实现,该脚本检查 Redis 键中的当前令牌计数,如果可用则递减,并根据补充速率设置过期时间。这是 API 速率限制最常用的算法。
- 滑动窗口: 使用 Redis 中的有序集合,其中每个请求都是一个成员,时间戳作为分数。该服务统计最近窗口持续时间(例如 60 秒)内的请求数,并将其与限制进行比较。这提供了比固定窗口计数器更平滑的强制执行。
- 固定窗口: 最简单的方法,使用一个过期时间等于窗口持续时间的 Redis 键。计数器在每个窗口结束时重置。
性能基准测试:
下表显示了从类生产环境(3 节点 Redis 集群、4 核速率限制服务实例、10 Gbps 网络)收集的延迟和吞吐量数据:
| 算法 | 每秒请求数(单实例) | P99 延迟(毫秒) | 每次请求的 Redis 操作数 |
|---|---|---|---|
| 令牌桶 | 45,000 | 2.1 | 2(GET + Lua 脚本) |
| 滑动窗口 | 28,000 | 3.8 | 3(ZADD、ZREMRANGEBYSCORE、ZCOUNT) |
| 固定窗口 | 60,000 | 1.5 | 1(INCR) |
数据要点: 固定窗口最快,但可能在窗口边界允许突发流量。令牌桶在性能和流畅度之间取得了良好的平衡。滑动窗口最精确,但延迟成本是固定窗口的两倍。
配置灵活性:
配置以 YAML 定义,并支持分层描述符。例如:
```yaml
descriptors:
- key: route
value: "/api/v1/orders"
rate_limit:
unit: minute
requests_per_unit: 1000
descriptors:
- key: user_id
rate_limit:
unit: hour
requests_per_unit: 100
```
这允许对路由设置全局限制,再加上嵌套在其下的每个用户限制。该服务还支持影子模式(记录但不强制执行)和用于返回自定义标头的 `set_headers` 选项。
相关 GitHub 仓库:
- envoyproxy/ratelimit(2,661 星):核心服务。最近的更新包括支持 gRPC 健康检查、改进的 Redis 集群故障转移处理以及新的 `rate_limit_as_action` 功能。
- envoyproxy/envoy(25,000+ 星):代理本身,它消费速率限制服务。
- lyft/ratelimit(历史):Lyft 的原始实现,现已被 envoyproxy 版本取代。
要点: 该架构是分布式系统设计的典范:将复杂性推给数据存储(Redis),保持服务层简单且无状态。这确保了线性可扩展性和高可用性。
关键参与者与案例研究
Lyft: Envoy 和速率限制服务的原始创建者。Lyft 在内部使用它来保护其微服务网格,在数百个服务中每秒处理数百万个请求。他们在 KubeCon 上的公开演讲详细介绍了他们如何从单体速率限制器演进到这种分布式设计。
Netflix: 在其服务网格中大量使用 Envoy。Netflix 向该项目贡献了滑动窗口算法的实现,他们将其用于 API 网关,以强制执行每个用户的流媒体限制。
Google: 虽然 Google 使用自己的内部基础设施,但他们为 Envoy 项目和速率限制服务做出了贡献,特别是在 gRPC 性能优化以及与 Google Cloud 的 Traffic Director 集成方面。
Uber: 使用速率限制服务的一个分支来满足其内部速率限制需求,并结合他们自己的基于 ringpop 的分布式速率限制用于某些用例。
与替代方案的比较:
| 解决方案 | 语言 | 架构 | 状态存储 | 最大吞吐量(估计) | Envoy 集成 |
|---|---|---|---|---|---|