一句话总结
90% 的场景用 x/time/rate,匀速场景用 uber-go/ratelimit,分布式用 sethvargo/go-limiter。
五大库速览
| 库 | 算法 | 分布式 | 一句话定位 |
|---|
| x/time/rate | 令牌桶 | ✗ | 标准库,首选 |
| uber-go/ratelimit | 漏桶 | ✗ | 匀速场景神器 |
| juju/ratelimit | 令牌桶 | ✗ | 老项目用,新项目别用 |
| sethvargo/go-limiter | 多种 | ✓ | 分布式首选 |
| throttled | 多种 | ✓ | HTTP 中间件开箱即用 |
核心库详解
1. x/time/rate:标准库答案
1
| limiter := rate.NewLimiter(10, 5)
|
三个核心方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| if !limiter.Allow() { http.Error(w, "rate limit", 429) return }
if err := limiter.Wait(ctx); err != nil { return err }
r := limiter.Reserve() time.Sleep(r.Delay())
|
常见坑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| limiter := rate.NewLimiter(100, 1)
func handler(w, r) { limiter := rate.NewLimiter(...) }
limiter.Wait(context.Background())
limiter := rate.NewLimiter(100, 200) var globalLimiter = rate.NewLimiter(100, 200) limiter.Wait(ctx)
|
2. uber-go/ratelimit:匀速场景
1 2 3 4 5 6
| rl := ratelimit.New(100)
for _, item := range items { rl.Take() process(item) }
|
为什么快:零内存分配,误差 < 1μs。
适合场景:定时任务同步、调用下游 API(保护下游不被打爆)。
不适合:用户请求限流(用户不想等)、突发流量场景。
3. sethvargo/go-limiter:分布式
1 2 3 4 5 6 7 8 9 10 11
| store, _ := memorystore.New(&memorystore.Config{ Tokens: 100, Interval: time.Minute, })
store, _ := redisstore.New(&redisstore.Config{ Client: redisClient, Tokens: 100, Interval: time.Minute, })
limiter, _ := limit.New(store, limit.Key("user_id"))
|
核心问题:Redis 延迟约 500μs(内存版 50ns),别用在核心链路。
降级策略:Redis 挂了,业务决定放行还是拒绝。
性能对比
1 2 3 4 5
| 单核 100 万次调用: x/time/rate 85ns/op 12M QPS uber-go 68ns/op 15M QPS ← 最快 sethvargo(memory) 280ns/op 3.6M QPS sethvargo(redis) 520μs/op 1.9K QPS ← 慢 6000 倍
|
选型决策树
1 2 3 4 5 6 7
| 需要分布式? ├─ 是 → sethvargo/go-limiter └─ 否 ↓
需要匀速(保护下游)? ├─ 是 → uber-go/ratelimit └─ 否 → x/time/rate(默认推荐)
|
5 个避坑指南
- 别用 per-IP 限流保护 per-user — 攻击者用 IP 池轻松绕过
- 限流配置要压测 — 配置 10000,实际撑 6000,雪崩就这么来的
- 限流后要有降级 — 返回缓存/默认值,别直接 429
- Wait 要传 ctx — 不传就永远不超时
- 限流要记录指标 — 不记录 = 没限流,出事复盘不了
4 层防御架构
1 2 3 4
| 1. CDN 限流(IP/国家) 2. API Gateway(user/api_key) 3. 服务内限流(业务接口) 4. 数据库限流(连接池)
|
单点限流 = 没用。