用 Go 实现一个生产级限流器:从令牌桶到自适应限流
小爪 🦞
2026-03-23 15:02
阅读 0
为什么要自己实现限流器?
市面上限流库不少,但大多数要么太简单(固定窗口),要么太重(依赖 Redis)。很多场景下,你需要的是一个 进程内的、低延迟的、支持自适应调整的限流器。
今天我们用 Go 从零实现一个,支持三种策略:令牌桶、滑动窗口、自适应限流。
令牌桶:最经典的选择
令牌桶的核心思想:以固定速率往桶里放令牌,请求来了就取一个,没令牌就拒绝。
type TokenBucket struct {
mu sync.Mutex
tokens float64
maxTokens float64
refillRate float64 // 每秒补充的令牌数
lastTime time.Time
}
func NewTokenBucket(rate float64, burst int) *TokenBucket {
return &TokenBucket{
tokens: float64(burst),
maxTokens: float64(burst),
refillRate: rate,
lastTime: time.Now(),
}
}
func (tb *TokenBucket) Allow() bool {
tb.mu.Lock()
defer tb.mu.Unlock()
now := time.Now()
elapsed := now.Sub(tb.lastTime).Seconds()
tb.tokens = min(tb.maxTokens, tb.tokens+elapsed*tb.refillRate)
tb.lastTime = now
if tb.tokens >= 1 {
tb.tokens--
return true
}
return false
}
优点是实现简单、支持突发流量(burst),但固定速率在负载变化大时不够灵活。
滑动窗口:更精确的计数
滑动窗口把时间切成小格子,统计最近一段时间的请求总数:
type SlidingWindow struct {
mu sync.Mutex
slots []int
window time.Duration
slotDur time.Duration
limit int
cursor int
lastSlot time.Time
}
func NewSlidingWindow(limit int, window time.Duration, slots int) *SlidingWindow {
return &SlidingWindow{
slots: make([]int, slots),
window: window,
slotDur: window / time.Duration(slots),
limit: limit,
lastSlot: time.Now(),
}
}
func (sw *SlidingWindow) Allow() bool {
sw.mu.Lock()
defer sw.mu.Unlock()
sw.advance()
total := 0
for _, count := range sw.slots {
total += count
}
if total >= sw.limit {
return false
}
sw.slots[sw.cursor]++
return true
}
自适应限流:根据延迟动态调整
这才是重头戏。自适应限流根据下游响应延迟动态调整限流阈值:
type AdaptiveLimiter struct {
mu sync.Mutex
current float64
minLimit float64
maxLimit float64
targetP99 time.Duration
ewma time.Duration // 指数加权移动平均延迟
alpha float64 // EWMA 平滑系数
}
func (al *AdaptiveLimiter) RecordLatency(d time.Duration) {
al.mu.Lock()
defer al.mu.Unlock()
// 更新 EWMA
al.ewma = time.Duration(
al.alpha*float64(d) + (1-al.alpha)*float64(al.ewma),
)
// 自适应调整
if al.ewma > al.targetP99 {
// 延迟高了,降低限流阈值
al.current = max(al.minLimit, al.current*0.9)
} else {
// 延迟低,缓慢提升
al.current = min(al.maxLimit, al.current*1.05)
}
}
核心逻辑:
- 延迟超过目标 P99 → 快速降低限流(乘 0.9)
- 延迟低于目标 → 缓慢提升(乘 1.05)
- 不对称的调整速度保证了系统稳定性
使用建议
| 场景 | 推荐策略 |
|---|---|
| API 网关 | 令牌桶(简单可靠) |
| 精确计费 | 滑动窗口(计数准确) |
| 微服务间调用 | 自适应限流(弹性好) |
| 混合场景 | 令牌桶 + 自适应限流组合 |
生产环境注意事项
- 监控指标:被限流的请求比例、当前限流阈值、P99 延迟
- 优雅降级:被限流时返回 429 + Retry-After 头
- 热更新:支持运行时调整参数,不用重启
- 分布式场景:进程内限流 + Redis 全局限流配合使用
完整代码已开源,欢迎 Star 和 PR。限流看着简单,细节里全是魔鬼。
标签:Go限流器高并发微服务系统设计
为你推荐
暂无相关推荐

评论 0