如何用Go语言编写一个高效的限流器(Rate Limiter)代码示例?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1083个文字,预计阅读时间需要5分钟。
Go 标准库不自带限流器,但官方维护的 time/rate 包已足够健壮、线程安全,并基于令牌桶(token bucket)模型,能应对突发流量,实现平滑限流。无需自行编写计数器或时间窗口等,以免引入漏桶、竞争、时钟漂移、重置逻辑等问题。
常见错误是把 Limiter 当成“每秒只允许 N 次调用”的开关,其实它更像一个带容量的水桶:每次 Allow() 或 Reserve() 都尝试取一个令牌,桶会按固定速率(rate.Limit)自动填充,满了就丢弃新令牌。
-
rate.Every(100 * time.Millisecond)表示每 100ms 补 1 个令牌,等价于10QPS - 初始化时第二个参数是桶容量(burst),设为
1就完全削平;设为5则允许短时 5 次连发 - 不要在 HTTP handler 里反复
new(rate.Limiter),应复用单例或按租户/路径分实例
Allow() 和 Reserve() 怎么选
Allow() 是最简接口:返回 bool,true 表示“此刻有令牌可取”,false 就该拒绝。适合无延迟容忍的场景(如 API 鉴权前置拦截)。
Reserve() 返回 *rate.Reservation,能告诉你“如果现在取,要等多久”,适合需要排队或预估延迟的逻辑(比如后台任务调度)。
本文共计1083个文字,预计阅读时间需要5分钟。
Go 标准库不自带限流器,但官方维护的 time/rate 包已足够健壮、线程安全,并基于令牌桶(token bucket)模型,能应对突发流量,实现平滑限流。无需自行编写计数器或时间窗口等,以免引入漏桶、竞争、时钟漂移、重置逻辑等问题。
常见错误是把 Limiter 当成“每秒只允许 N 次调用”的开关,其实它更像一个带容量的水桶:每次 Allow() 或 Reserve() 都尝试取一个令牌,桶会按固定速率(rate.Limit)自动填充,满了就丢弃新令牌。
-
rate.Every(100 * time.Millisecond)表示每 100ms 补 1 个令牌,等价于10QPS - 初始化时第二个参数是桶容量(burst),设为
1就完全削平;设为5则允许短时 5 次连发 - 不要在 HTTP handler 里反复
new(rate.Limiter),应复用单例或按租户/路径分实例
Allow() 和 Reserve() 怎么选
Allow() 是最简接口:返回 bool,true 表示“此刻有令牌可取”,false 就该拒绝。适合无延迟容忍的场景(如 API 鉴权前置拦截)。
Reserve() 返回 *rate.Reservation,能告诉你“如果现在取,要等多久”,适合需要排队或预估延迟的逻辑(比如后台任务调度)。

