Go并发限流核心用rate.Limiter实现令牌桶算法,支持接口级限流(Allow/Wait)、多goroutine统一排队、用户维度细粒度限流及分布式方案,需避免重复创建、合理选型阻塞策略并添加监控头与日志。

在 Go 中实现并发任务限流,核心是控制单位时间内执行的任务数量或请求频率,避免系统过载。最常用、最自然的方式是使用 golang.org/x/time/rate 包(即 令牌桶算法),它轻量、线程安全、精度高,适合接口限流、后台任务节制等场景。
用 rate.Limiter 实现简单接口级限流
每个请求到来时尝试获取一个令牌,获取失败则拒绝或等待。适用于 HTTP 中间件或 RPC 入口:
- 创建限流器:例如每秒最多 100 个请求,初始桶容量为 10(允许短时突发)
limiter := rate.NewLimiter(100, 10) - 在 handler 中检查:
if !limiter.Allow() { http.Error(w, "Too Many Requests", http.StatusTooManyRequests); return } - 更稳妥的做法是用
Wait阻塞等待可用令牌(适合后台任务),或Reserve判断后手动 sleep(精细控制超时)
为并发 Goroutine 任务统一限流
当启动大量 goroutine 执行外部调用(如批量发短信、调第三方 API)时,不能靠单个 limiter 拦截入口,而需在任务执行前统一“排队”:
- 定义全局 limiter:
var taskLimiter = rate.NewLimiter(5, 10) // 每秒最多 5 个任务,最多积压 10 个 - 每个 goroutine 启动前调用
taskLimiter.Wait(ctx)—— 自动阻塞直到拿到令牌 - 配合
context.WithTimeout可避免无限等待,例如:ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
err := taskLimiter.Wait(ctx)
if err != nil { /* 超时跳过该任务 */ }
按用户/租户维度做细粒度限流
单一全局限流无法满足多租户或用户级配额需求。此时需用 sync.Map 或带 TTL 的内存缓存(如 github.com/go-redis/redis)维护每个 key 的独立限流器:
立即学习“go语言免费学习笔记(深入)”;
- 用
map[string]*rate.Limiter+sync.RWMutex管理(注意:rate.Limiter 本身可复用,但 map 非线程安全) - 示例 key:用户 ID、API 路径、IP 地址等;定期清理过期项(如 1 小时无访问则删除)
- 更推荐用
github.com/uber-go/ratelimit(滑动窗口)或结合 Redis 的INCR + EXPIRE实现分布式限流(跨进程/实例)
避免常见陷阱
限流不是加个 if 就完事,几个关键细节容易被忽略:
- 不要在循环里重复 new Limiter:每次 new 都重置桶状态,失去限流意义;应复用同一个实例或按 key 复用
-
Allow() 不阻塞,但可能让瞬时流量冲垮下游:适合“快速失败”场景;高一致性要求用
Wait或ReserveN -
HTTP 限流要返回标准头:如
X-RateLimit-Limit、X-RateLimit-Remaining、Retry-After,方便客户端感知 - 日志和监控不可少:记录被限流的请求量、key、时间,便于分析是否配置过严或遭遇攻击










