基于 Redis 的滑动窗口限流器

2.6k words

速率限制库 (Rate Limiter)

简介

go-toolkit 项目中的 ratelimit 包提供了一个高性能、可靠的基于 Redis 的速率限制实现,使用滑动窗口算法确保平滑且精确的请求限流。该库专为高并发环境设计,支持分布式部署,并提供完整的监控集成。本文展示了在10,000 QPS限流设置下的性能表现。

安装方式

1
go get github.com/go-toolkit/pkg/ratelimit

项目地址

https://github.com/JarrettGuo/go-toolkit

主要特点

  • 高性能:在标准配置下可处理 20,000+ 请求/秒
  • 可靠性:使用 Redis 确保分布式环境下的数据一致性
  • 滑动窗口算法:相比固定窗口更平滑,避免边界突发问题
  • 易于集成:与 Gin 框架无缝集成的中间件
  • 可观测性:内置 Prometheus 指标,配套 Grafana 仪表板
  • 完整测试:全面的单元测试和集成测试覆盖

核心实现

该限流器基于Redis的有序集合(ZSET)实现滑动窗口算法,使用Lua脚本确保原子性操作:

1
2
3
4
5
6
7
8
9
10
11
// 创建 Redis 客户端
redisClient := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})

// 创建限流器:每秒最多允许 10000 个请求
limiter := ratelimit.NewRedisSlidingWindowLimiter(redisClient, time.Second, 10000)

// 在 Gin 中使用
r := gin.Default()
r.Use(RateLimitMiddleware(limiter))

性能测试结果

以下是在每秒限制10000请求的设置下,不同并发条件的性能表现:

测试 请求数 并发数 RPS 成功率 耗时
1 1000 5 11574.88 100.00% 0.086 seconds
2 2000 10 13961.31 100.00% 0.143 seconds
3 5000 25 17043.96 100.00% 0.293 seconds
4 7500 35 19270.64 100.00% 0.389 seconds
5 10000 50 19061.05 100.00% 0.525 seconds
6 15000 75 20270.43 66.66% 0.740 seconds
7 20000 100 21140.80 50.00% 0.946 seconds

ab测试

分析:从表格数据可以看出,随着并发度的增加,RPS持续提高,最大达到21k+。在请求数量低于限流阈值(10000/秒)时,成功率保持100%。当请求量超过限流阈值时(测试6和7),成功率如预期下降,验证了限流器的有效性。即使在高负载下,系统性能仍然保持稳定增长,没有出现崩溃或性能断崖。

低并发测试 (2线程,3连接)

1
wrk -t2 -c3 -d30s http://localhost:8080/limited/hello
  • 平均延迟: 215.97µs
  • 请求/秒: 20,019.22
  • 总请求: 601,161 (30.03秒)
  • 非成功响应: 301,131 (被限流的请求)

低并发测试
低并发测试可视化

分析:在这个轻量级负载测试中,尽管并发连接数很低,系统仍然展现出优秀的性能 - 平均延迟仅215微秒,吞吐量达到约20k QPS。值得注意的是,系统总共处理了60万请求,其中约30万被成功限流(50.1%),这与10,000/秒的限流设置一致。这表明即使在低并发场景下,限流器也能精确控制流量,同时保持极低的处理延迟。

高负载测试 (12线程,500连接)

1
wrk -t12 -c500 -d30s http://localhost:8080/limited/hello
  • 平均延迟: 18.52ms
  • 请求/秒: 27,245.18
  • 总请求: 818,802 (30.05秒)
  • 被限流请求: ~63.4% (根据Grafana面板)

高负载测试
高负载测试可视化

分析:这个高负载测试将并发连接数提高到500,模拟高流量场景。尽管面临大量并发请求,系统延迟仅增加到18.52毫秒,仍然维持了27k+ QPS的处理能力。被限流请求的比例增加到63.4%,这是因为实际请求速率(27k/秒)远超过限流阈值(10k/秒)。结果表明,即使在极高并发负载下,限流器依然高效运行,并不会成为系统瓶颈。Grafana面板显示请求速率和限流数量都保持稳定,没有出现剧烈波动,证明了滑动窗口算法的平滑特性。

延迟分布测试 (4线程,10连接)

1
wrk -t4 -c10 -d30s --latency http://localhost:8080/limited/hello
  • 50%响应时间: 298.00µs
  • 75%响应时间: 359.00µs
  • 90%响应时间: 485.00µs
  • 99%响应时间: 8.53ms
  • 请求/秒: 23,558.53

延迟分布
延迟分布可视化

分析:这个测试专注于请求延迟分布,结果令人印象深刻。即使在持续的中等负载下,90%的请求都能在不到0.5毫秒内完成,99%的请求也只需8.53毫秒。这种一致的低延迟表现对于延迟敏感型应用尤为重要。系统同时维持了约23.5k QPS的吞吐量,高于设定的10k限流阈值,导致约57.6%的请求被限流。延迟分布曲线平稳,没有出现长尾效应,说明限流机制对系统性能影响很小,不会导致请求处理时间的突然增加。

监控能力

该库提供了完整的监控指标,通过Prometheus集成,可在Grafana中实现可视化:

  • 请求速率监控:实时跟踪API请求速率变化,即使在流量剧烈波动时也能保持可见性
  • 被限流请求统计:精确统计被限流的请求数量,便于调整限流策略
  • 限流百分比:通过仪表盘直观显示被限流请求占比,一目了然
  • 请求延迟监控:P95请求延迟实时监控,帮助及时发现性能问题

结论

go-toolkit/ratelimit包提供了一个高性能、生产就绪的速率限制解决方案,适用于API网关、微服务和任何需要请求控制的场景。测试数据显示,该实现即使在高并发下也能保持稳定的性能,同时提供了精确的请求限流能力。

该库的滑动窗口算法确保了更加平滑的限流体验,避免了传统固定窗口算法在窗口边界可能出现的突发流量问题。在各种负载条件下,系统都展现出卓越的性能和可靠性,是构建可靠、高性能Web服务的理想选择。

通过测试结果可以确信,即使面对超过限流阈值数倍的请求流量,该限流器也能保持高效运行,确保系统稳定性不受影响。这对于构建能够应对流量峰值的弹性系统至关重要。