
1) 【一句话结论】:设计高并发API限流系统,核心是结合滑动窗口(精确控制QPS,应对突发流量)与令牌桶(平滑流量,允许短时间超量),搭配熔断(错误率超阈值时快速失败)和降级(服务不可用时降级),在Golang中通过并发安全的数据结构(如环形数组、channel)与分布式缓存(如Redis)实现,分布式场景下通过共享存储保证全局计数一致性。
2) 【原理/概念讲解】:老师口吻解释关键概念:
3) 【对比与适用场景】:
| 特性 | 滑动窗口(滑动计数) | 令牌桶(速率限制) |
|---|---|---|
| 定义 | 基于时间窗口的请求计数,统计当前窗口内的请求数 | 按固定速率生成令牌,请求消耗令牌,桶满则等待 |
| 特性 | 精确控制QPS,对突发流量敏感(窗口内计数) | 平滑流量,允许短时间超量(桶内令牌数) |
| 适用场景 | 需要严格限制QPS,避免突发流量冲击(如API接口) | 需要平滑流量,允许短时间波动(如用户登录、支付) |
| 注意点 | 窗口大小需平衡精度与延迟(太大延迟,太小计数不准) | 令牌生成速率需准确计算(否则无法平滑流量) |
4) 【示例】:
type SlidingWindowLimiter struct {
windowSize int // 时间窗口数量(秒)
bucketSize int // 每个窗口最大请求数
buckets []int // 环形数组存储每个窗口的请求数
curIndex int // 当前窗口索引
mu sync.Mutex // 并发控制
}
func (l *SlidingWindowLimiter) Allow() bool {
l.mu.Lock()
defer l.mu.Unlock()
l.curIndex = (l.curIndex + 1) % l.windowSize
if l.buckets[l.curIndex] >= l.bucketSize {
return false
}
l.buckets[l.curIndex]++
return true
}
type TokenBucketLimiter struct {
capacity int // 桶容量(最大令牌数)
rate int // 令牌生成速率(每秒生成令牌数)
tokens int // 当前令牌数
mu sync.Mutex // 并发控制
ticker *time.Ticker // 定时器生成令牌
}
func (l *TokenBucketLimiter) Allow() bool {
l.mu.Lock()
defer l.mu.Unlock()
l.tokens = min(l.tokens + l.rate, l.capacity)
if l.tokens > 0 {
l.tokens--
return true
}
return false
}
5) 【面试口播版答案】:
“面试官您好,设计高并发API限流系统,核心是结合滑动窗口(精确控制QPS,应对突发流量)与令牌桶(平滑流量,允许短时间超量),搭配熔断(错误率超阈值时快速失败)和降级(服务不可用时降级)。在Golang中,滑动窗口用环形数组存储时间窗口的请求数,用互斥锁保护并发,滑动时间窗口时更新计数;令牌桶用定时器按固定速率生成令牌,请求时检查令牌数。熔断机制用状态机(正常/熔断/恢复),用Redis存储状态,错误率超过阈值时切换到熔断状态,后续请求直接失败。分布式场景下,通过Redis的分布式计数器(如ZADD或SETNX+过期时间)保证全局计数一致,避免单点故障。整体策略是:滑动窗口用于精确控制QPS,令牌桶用于平滑流量,熔断防止雪崩,降级保证核心功能可用。”
6) 【追问清单】:
7) 【常见坑/雷区】: