流量整形和限流令牌桶算法
FreeGuideOnline
最新
2026-07-12
class TokenBucket: capacity // 最大令牌数 tokens // 当前令牌数(初始为 capacity) rate // 令牌生成速率(个/秒) last_refill_time // 上次添加令牌的时间戳
def allow_request():
now = current_time()
// 计算从上次补充到现在应该生成的令牌数
elapsed = now - last_refill_time
tokens = min(capacity, tokens + elapsed * rate)
last_refill_time = now
if tokens >= 1:
tokens -= 1
return true // 允许请求
else:
return false // 限流
注意:实际实现中通常使用惰性补充(仅在判断时计算补充),以减少定时器资源消耗。
### 不同场景的参数设计
- **平滑限流**:设置较小的 `b`(如等于 `r`),突发少。类似漏桶效果。
- **允许突发**:设置较大的 `b`(如 `3*r`),让系统能应对短时高峰。
- **完全无限制**:`b` 极大,直到系统资源耗尽后才限流,仅适合测试。
## 令牌桶的编程实现(Go 示例)
下面用 Go 语言实现一个线程安全的令牌桶,利用 `time.Ticker` 定时添加令牌,也可以选择惰性计算。
### 基于惰性补充的版本
```go
type TokenBucket struct {
capacity float64
tokens float64
rate float64 // 每秒生成令牌数
lastRefill time.Time
mu sync.Mutex
}
func NewTokenBucket(rate, capacity float64) *TokenBucket {
return &TokenBucket{
capacity: capacity,
tokens: capacity, // 初始满桶
rate: rate,
lastRefill: time.Now(),
}
}
func (tb *TokenBucket) Allow() bool {
tb.mu.Lock()
defer tb.mu.Unlock()
now := time.Now()
elapsed := now.Sub(tb.lastRefill).Seconds()
// 补充令牌
tb.tokens += elapsed * tb.rate
if tb.tokens > tb.capacity {
tb.tokens = tb.capacity
}
tb.lastRefill = now
if tb.tokens >= 1 {
tb.tokens--
return true
}
return false
}
使用方式:
limiter := NewTokenBucket(10, 20) // 每秒10个,允许最大突发20个
if limiter.Allow() {
handleRequest()
} else {
http.Error(w, "too many requests", 429)
}