流量整形和限流令牌桶算法

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)
}