跳转到主内容
极星编程网:以代码为星,赴技术山海!

Go语言怎么写API限流器_Go语言限流器实战教程【实战】

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

相关文章