golang.org/x/time/rate 足够健壮,推荐直接使用;需复用同一实例、避免混用 AllowN/WaitN、burst 不能为0、按IP/用户限流需可信标识、WaitN 是主调用方式、rate.Limit 单位为事件/秒、它是令牌桶实现。
限流器用
就够了,别自己造轮子
标准库没有限流能力,但官方维护的
包足够健壮、线程安全、内存友好,Go 1.20+ 项目直接用它就行。自己基于
或
实现容易漏掉并发边界或时间漂移问题。
常见错误现象:
被多个 goroutine 共享时没出错,但误以为要加锁;或者把
和
混用导致阻塞逻辑失控。
表示「每秒最多 10 次,初始令牌数为 1」,不是「10 QPS + 1 并发」
是推荐的主调用方式,它会自动 sleep 到有足够令牌,比
+ 手动重试更可靠
注意
参数不能设为 0:设成 0 会导致所有请求立刻被拒,因为初始令牌数为 0 且无法累积
HTTP 中间件里怎么嵌入
别在每个 handler 里 new 一个
,那等于没限流——每个请求都拿到独立的令牌桶。必须复用同一个实例,通常挂到
的字段或全局变量上。
使用场景:API 网关、微服务入口、管理后台接口等需要统一速率控制的地方。
立即学习
“
go语言免费学习笔记(深入)
”;
按 IP 限流?得自己从
提取真实 IP(注意 X-Forwarded-For 头是否可信),再用
管理每个 IP 对应的
按用户 ID 限流?确保 ID 来源可信(如 JWT 解析后取
字段),避免被伪造绕过
不要在
内部调用
后忽略返回 error —— 如果 ctx 超时,
返回
,必须显式返回 429 或 503
和
怎么配才不翻车
类型本质是
,单位是「事件/秒」,不是「次/分钟」或「个/毫秒」。写成
看似直观,其实等价于
,但隐含了精度损失和可读性陷阱。
go语言参考手册 中文CHM版
Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易。本文给大家带来Go参考手册,需要的可以来下载! Go是从2007年末由Robert Griesemer, Rob Pike, Ken Thompson主持开发,后来还加入了Ian Lance Taylor, Russ Cox等人,并最终于2009年11月开源,在2012年早些时候发布了Go 1稳定版本。现在Go的开发已经是完全开放的,并且拥有一个活跃的社区。 Go 语言特色 简洁、快速、安全 并行、有趣、开源 内存管理、v数组安全、编译
下载
性能影响:高 QPS 场景下,
会多一次浮点除法,虽然微乎其微,但参数含义不直白,容易传错。
想表达「每 200ms 最多 1 次」→ 直接写
,别用
内部会做
计算,如果传入
或负值,结果是
或
,导致限流器失效
测试时用
模拟时间推进?别碰——
内部用的是
,mock 需要替换整个 time 包或改用
(Go 1.22+)
漏桶 vs 令牌桶,
到底是哪个
它是令牌桶(token bucket),不是漏桶(leaky bucket)。区别在于:令牌桶允许突发流量(burst 容量内),漏桶强制匀速输出。Go 的实现支持预充令牌、支持非整数速率、支持阻塞等待,典型令牌桶行为。
容易踩的坑:看到 burst 参数就以为能扛住短时洪峰,但没意识到 burst 只是“缓冲区大小”,不解决后端处理能力瓶颈。比如 burst=100,但下游数据库只能扛 20 QPS,限流器放行后照样雪崩。
别把
设得远大于平均 QPS:设成 1000 而平均只有 10,等于给攻击者留了放大窗口
监控重点不是「被拒绝次数」,而是「平均等待时长」——如果
平均耗时持续 >100ms,说明令牌生成跟不上消耗,该降级或扩容了
不用考虑「动态调整 rate」:运行时修改
是线程安全的,但频繁调用会影响性能,适合按小时/天粒度调整,不适合 per-request 动态策略
事情说清了就结束。真正难的从来不是怎么写限流器,而是判断该在哪一层限、对谁限、限完之后怎么反馈、失败了往哪打日志、指标怎么跟 Prometheus 对齐——这些都没法靠一个
自动搞定。
golang.org/x/time/rateratetime.Tickersync.Mutexrate.LimiterAllow()WaitN()rate.NewLimiter(rate.Limit(10), 1)WaitN(ctx, n)AllowN()burstrate.Limiterrate.Limiterhttp.Handlerr.RemoteAddrsync.Map*rate.Limitersubhttp.HandlerFunclimiter.WaitN(ctx, 1)WaitNcontext.DeadlineExceededrate.Limittime.Durationrate.Limitfloat64rate.Every(100 * time.Millisecond)rate.Limit(10)rate.Everyrate.Limit(5)rate.Every(200 * time.Millisecond)rate.Every1 / d.Seconds()0+Inf-Inftime.Now().Add(...)rate.Limitertime.Now()rate.NewLimiterWithClockrate.LimiterburstWaitNlimiter.SetLimitAndBurst()rate.Limiter