Go 限流库选型:从入门到实战

chihqiang chihqiang 2026-07-13 约 6 分钟 2276 字

一句话总结

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)  // 每秒 10 个,桶容量 5

三个核心方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 1. 非阻塞,能放就放
if !limiter.Allow() {
http.Error(w, "rate limit", 429)
return
}

// 2. 阻塞等,配合 ctx 超时
if err := limiter.Wait(ctx); err != nil {
return err
}

// 3. 预留,告诉你要等多久
r := limiter.Reserve()
time.Sleep(r.Delay())

常见坑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ❌ burst 太小,正常流量也被砍
limiter := rate.NewLimiter(100, 1) // burst=1

// ❌ 每个请求新建 limiter,限流失效
func handler(w, r) {
limiter := rate.NewLimiter(...) // 每次都新建
}

// ❌ Wait 不传 ctx,永远不超时
limiter.Wait(context.Background())

// ✅ 正确姿势
limiter := rate.NewLimiter(100, 200) // burst = rate * 2
var globalLimiter = rate.NewLimiter(100, 200) // 全局
limiter.Wait(ctx) // 传 ctx

2. uber-go/ratelimit:匀速场景

1
2
3
4
5
6
rl := ratelimit.New(100)  // 每秒 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,
})

// Redis 版
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 个避坑指南

  1. 别用 per-IP 限流保护 per-user — 攻击者用 IP 池轻松绕过
  2. 限流配置要压测 — 配置 10000,实际撑 6000,雪崩就这么来的
  3. 限流后要有降级 — 返回缓存/默认值,别直接 429
  4. Wait 要传 ctx — 不传就永远不超时
  5. 限流要记录指标 — 不记录 = 没限流,出事复盘不了

4 层防御架构

1
2
3
4
1. CDN 限流(IP/国家)
2. API Gateway(user/api_key)
3. 服务内限流(业务接口)
4. 数据库限流(连接池)

单点限流 = 没用

分享: Twitter Facebook
chihqiang

chihqiang

代码与生活的点滴记录