go并发限流应区分频次与并发数:频次用rate.limiter(漏桶+令牌桶混合,支持预热),并发数用semaphore.weighted(带权重信号量,需注意acquire/release配对);切勿手写令牌桶或滥用mutex。

Go 里做并发限流,别直接上 sync.Mutex 或自己手写计数器——它既难保正确性,又容易在高并发下成为瓶颈。真正靠谱的方式是用 golang.org/x/sync/semaphore(信号量)或基于 time.Ticker + chan 的令牌桶,再或者用成熟的第三方库如 golang.org/x/time/rate。
用 rate.Limiter 实现请求级限流最简单也最常用
这是标准库扩展中最推荐的方案,适合 HTTP handler、RPC 调用、数据库连接池入口等场景。它底层是漏桶+令牌桶混合模型,线程安全,且支持预热(AllowN 可跳过初始等待)。
常见错误是误以为 rate.NewLimiter(10, 1) 表示“每秒最多 10 次”,其实第二个参数是 burst(突发容量),必须 ≥ 1;若设为 0,Allow() 永远返回 false。
-
rate.NewLimiter(rate.Every(100*time.Millisecond), 3):每 100ms 放行 1 个令牌,最多攒 3 个,即平均 QPS=10,最大瞬时并发=3 - 阻塞等待用
limiter.Wait(ctx),非阻塞用limiter.Allow()或limiter.AllowN(time.Now(), n) - 注意:HTTP handler 中传入的
ctx应带 timeout,否则Wait可能永久挂起
semaphore.Weighted 更适合资源型并发控制
当你需要限制的是“同时运行的 goroutine 数量”(比如并发调用外部 API、打开文件句柄、持有 DB 连接),而不是请求频次,semaphore.Weighted 比 rate.Limiter 更贴切——它是带权重的信号量,支持异步获取、可取消、不依赖时间。
立即学习“go语言免费学习笔记(深入)”;
本文档主要讲述的是android rtsp流媒体播放介绍;实时流协议(RTSP)是应用级协议,控制实时数据的发送。RTSP提供了一个可扩展框架,使实时数据,如音频与视频,的受控、点播成为可能。数据源包括现场数据与存储在剪辑中数据。该协议目的在于控制多个数据发送连接,为选择发送通道,如UDP、组播UDP与TCP,提供途径,并为选择基于RTP上发送机制提供方法。希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
典型陷阱是忘记调用 Release(),导致资源永远无法归还;或在 defer 中释放但没判断 acquire 是否成功。
- 初始化:
sem := semaphore.NewWeighted(5)表示最多 5 个并发 - 获取:
if err := sem.Acquire(ctx, 1); err != nil { return }—— 必须检查 err - 释放:
defer sem.Release(1),且 release 的 weight 必须和 acquire 一致 - 支持非整数权重(如大任务占 3 单位,小任务占 1 单位),但需自行管理语义
自己实现令牌桶要注意时钟漂移与精度丢失
除非有特殊定制需求(比如动态调整速率、多维度配额),否则不建议从零写令牌桶。手写容易出错的点集中在时间计算上:用 time.Since() 算间隔再乘速率,会因浮点误差累积导致令牌数偏差;用 time.Now().UnixNano() 做差虽精确,但需注意纳秒转秒的除法截断。
另一个坑是未加锁读写共享的令牌数和上次填充时间,造成竞态——即使只读字段,在 32 位系统上 int64 读写也不是原子的。
- 必须用
sync/atomic操作int64类型的时间戳和令牌数 - 填充逻辑应统一在每次
Allow()前触发,避免多 goroutine 重复填充 - 测试时用
runtime.GOMAXPROCS(1)+ 大量 goroutine 并发调用,比单协程更能暴露竞态
真正难的不是选哪个工具,而是厘清你要限的是「频次」还是「并发数」,再决定用 rate.Limiter 还是 semaphore.Weighted。混淆这两者,后面调参、压测、线上排障都会事倍功半。









