Documentation
¶
Overview ¶
Package ratelimit 提供通用限流器实现。
支持令牌桶和滑动窗口两种限流算法,以及按 key 隔离的限流器管理; Lockout 提供按 key 的防爆破锁(fail2ban 形态:失败计数达阈值 → 锁定拒绝态)。
用法:
// 令牌桶限流器
limiter := ratelimit.NewTokenBucket(100, 200) // 100 QPS, burst 200
if limiter.Allow() {
// 处理请求
}
// 按 key 隔离的限流器(如按用户 ID)
keyed := ratelimit.NewKeyedLimiter(func() ratelimit.Limiter {
return ratelimit.NewTokenBucket(10, 20)
}, 10*time.Minute)
if keyed.Allow("user:123") {
// 处理请求
}
// 防爆破锁(登录/API Key 防爆破、匿名接口防枚举)
lo := ratelimit.NewLockout(ratelimit.LockoutConfig{
Window: 10 * time.Minute, Threshold: 5, LockDuration: 15 * time.Minute,
})
if locked, remain := lo.CheckLocked("user:42"); locked {
// 对外 429 + 剩余秒数文案(remain 即剩余锁定时长)
}
if locked, _ := lo.RecordFailure("user:42"); locked {
// 本次失败触发锁定(或已在锁定中)
}
lo.Reset("user:42") // 登录成功清零
Index ¶
- type KeyedLimiter
- type KeyedLimiterOf
- func (kl *KeyedLimiterOf[K]) Allow(key K) bool
- func (kl *KeyedLimiterOf[K]) AllowN(key K, n int) bool
- func (kl *KeyedLimiterOf[K]) AllowWithRetryAfter(key K) (wait time.Duration, ok bool)
- func (kl *KeyedLimiterOf[K]) Close()
- func (kl *KeyedLimiterOf[K]) Delete(key K)
- func (kl *KeyedLimiterOf[K]) Get(key K) Limiter
- func (kl *KeyedLimiterOf[K]) Len() int
- func (kl *KeyedLimiterOf[K]) OverCapHits() int64
- func (kl *KeyedLimiterOf[K]) Replace(key K, l Limiter)
- type KeyedOption
- type Limiter
- type Lockout
- type LockoutConfig
- type LockoutOf
- func (l *LockoutOf[K]) CheckLocked(key K) (locked bool, lockRemain time.Duration)
- func (l *LockoutOf[K]) Close()
- func (l *LockoutOf[K]) Len() int
- func (l *LockoutOf[K]) OverCapHits() int64
- func (l *LockoutOf[K]) RecordFailure(key K) (locked bool, lockRemain time.Duration)
- func (l *LockoutOf[K]) Reset(key K)
- type SlidingWindow
- type TokenBucket
- type TokenBucketOption
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type KeyedLimiter ¶
type KeyedLimiter = KeyedLimiterOf[string]
KeyedLimiter 是 string key 的别名,兼容既有调用方。
func NewKeyedLimiter ¶
func NewKeyedLimiter(factory func() Limiter, ttl time.Duration) *KeyedLimiter
NewKeyedLimiter 创建 string key 的限流器管理器(兼容旧 API)。
参数:
- factory: 创建新限流器的工厂函数
- ttl: 限流器空闲过期时间,超过此时间未使用的限流器会被清理
用法:
keyed := ratelimit.NewKeyedLimiter(func() ratelimit.Limiter {
return ratelimit.NewTokenBucket(10, 20)
}, 10*time.Minute)
type KeyedLimiterOf ¶ added in v0.5.0
type KeyedLimiterOf[K comparable] struct { // contains filtered or unexported fields }
KeyedLimiterOf 是按 key 隔离的限流器管理器(key 类型可定制)。 每个 key 维护独立的限流器实例,支持空闲回收、容量护栏、速率热更。
func NewKeyedLimiterOf ¶ added in v0.5.0
func NewKeyedLimiterOf[K comparable](factory func() Limiter, opts ...KeyedOption[K]) *KeyedLimiterOf[K]
NewKeyedLimiterOf 创建按 key 隔离的限流器管理器(key 任意 comparable 类型)。
用法:
keyed := ratelimit.NewKeyedLimiterOf[uint](
func() ratelimit.Limiter { return ratelimit.NewTokenBucket(10, 20) },
ratelimit.WithIdleTTL[uint](10*time.Minute),
ratelimit.WithMaxEntries[uint](10_000),
)
func (*KeyedLimiterOf[K]) Allow ¶ added in v0.5.0
func (kl *KeyedLimiterOf[K]) Allow(key K) bool
Allow 检查指定 key 是否允许请求。容量护栏拒绝未登记 key 时返回 false。
func (*KeyedLimiterOf[K]) AllowN ¶ added in v0.5.0
func (kl *KeyedLimiterOf[K]) AllowN(key K, n int) bool
AllowN 检查指定 key 是否允许 N 个请求。容量护栏拒绝未登记 key 时返回 false。
func (*KeyedLimiterOf[K]) AllowWithRetryAfter ¶ added in v0.5.0
func (kl *KeyedLimiterOf[K]) AllowWithRetryAfter(key K) (wait time.Duration, ok bool)
AllowWithRetryAfter 检查是否放行;拒绝时返回建议等待时长(不消耗令牌的探测)。
- ok=true:请求放行,wait=0
- ok=false:请求拒绝;wait 是下一个可用令牌的等待时长(Reserve→Delay→Cancel, 不消耗令牌),容量护栏拒绝时返回建议 Retry-After
探测语义:被拒请求不会自我消耗配额,避免放大限流(429 + Retry-After 场景)。
func (*KeyedLimiterOf[K]) Close ¶ added in v0.5.0
func (kl *KeyedLimiterOf[K]) Close()
Close 关闭限流器管理器,停止清理 goroutine。可安全重复调用。
func (*KeyedLimiterOf[K]) Delete ¶ added in v0.5.0
func (kl *KeyedLimiterOf[K]) Delete(key K)
Delete 删除指定 key 的限流器。
func (*KeyedLimiterOf[K]) Get ¶ added in v0.5.0
func (kl *KeyedLimiterOf[K]) Get(key K) Limiter
Get 获取指定 key 的限流器。表满且 key 未登记时返回 nil。
func (*KeyedLimiterOf[K]) Len ¶ added in v0.5.0
func (kl *KeyedLimiterOf[K]) Len() int
Len 返回当前活跃的限流器数量。
func (*KeyedLimiterOf[K]) OverCapHits ¶ added in v0.5.0
func (kl *KeyedLimiterOf[K]) OverCapHits() int64
OverCapHits 返回容量护栏拒绝未登记 key 的累计次数(可观测性)。
func (*KeyedLimiterOf[K]) Replace ¶ added in v0.5.0
func (kl *KeyedLimiterOf[K]) Replace(key K, l Limiter)
Replace 将指定 key 的限流器替换为新实例(速率热更)。 换桶即新配额,桶内余量归零(调用方接受)。key 不存在时等价登记新桶。
type KeyedOption ¶ added in v0.5.0
type KeyedOption[K comparable] func(*keyedConfig[K])
KeyedOption 是 KeyedLimiterOf 的函数选项。
func WithIdleTTL ¶ added in v0.5.0
func WithIdleTTL[K comparable](ttl time.Duration) KeyedOption[K]
WithIdleTTL 设置空闲回收周期;表满时也先触发回收再判满。 <=0 使用默认 1 分钟。
func WithMaxEntries ¶ added in v0.5.0
func WithMaxEntries[K comparable](max int) KeyedOption[K]
WithMaxEntries 设置容量护栏上限(未登记 key 在表满时被拒绝,已登记 key 不受影响)。 0 = 不限(默认)。
func WithOverCapRetryAfter ¶ added in v0.5.0
func WithOverCapRetryAfter[K comparable](d time.Duration) KeyedOption[K]
WithOverCapRetryAfter 设置容量护栏拒绝时 AllowWithRetryAfter 返回的建议等待。 <=0 使用默认 1 分钟。
type Limiter ¶
type Limiter interface {
// Allow 检查是否允许一个请求。
Allow() bool
// AllowN 检查是否允许 N 个请求。
AllowN(n int) bool
// Reserve 预留一个令牌,返回需要等待的时间。
// 如果返回 0 表示立即可用。
Reserve() time.Duration
}
Limiter 是通用限流器接口。
type Lockout ¶ added in v0.11.0
Lockout 是 string key 的别名,兼容惯例(对齐 KeyedLimiter)。
func NewLockout ¶ added in v0.11.0
func NewLockout(cfg LockoutConfig) *Lockout
NewLockout 创建 string key 的防爆破锁。 Window/Threshold/LockDuration 为必填语义参数,非法值直接 panic (配置错误宁可启动即暴露——锁静默失效等于没有防护)。
type LockoutConfig ¶ added in v0.11.0
type LockoutConfig struct {
// Window 为失败计数窗口:窗口内的失败事件按 key 累计(滑动窗口,
// 半开区间 (now-Window, now],窗口外样本自然老化)。
Window time.Duration
// Threshold 为锁定阈值:窗口内第 N 次失败触发锁定。
Threshold int
// LockDuration 为锁定时长:触发后 key 进入拒绝态持续 L 秒,期间 CheckLocked
// 返回剩余时长(供对外 429 文案);到期自动解除。后续失败不延长锁定。
LockDuration time.Duration
// MaxEntries 为容量护栏上限(0 = 不限,对齐 KeyedLimiterOf.WithMaxEntries)。
// 表满且 key 未登记时失败样本被丢弃(fail-open + OverCapHits 观测)——
// 不因内部容量问题拒绝认证尝试(那会放大成对合法用户的拒绝服务)。
MaxEntries int
// IdleTTL 为空闲回收周期;<=0 用默认 1 分钟。锁定中的条目免于回收
// (提前驱逐等于提前解锁);回收以空闲计时为准,不改变锁定到期语义。
IdleTTL time.Duration
// Now 为时钟注入(nil = time.Now;与 budget.WithClock 同款,
// 假时钟测试完全确定)。清扫节奏仍走真实 ticker,到期/空闲判定一律用本时钟。
Now func() time.Time
}
LockoutConfig 是 Lockout 的构造配置。
type LockoutOf ¶ added in v0.11.0
type LockoutOf[K comparable] struct { // contains filtered or unexported fields }
LockoutOf 是按 key 隔离的防爆破锁(fail2ban 形态):窗口内失败事件达阈值 → key 进入锁定拒绝态持续 LockDuration(期间即使凭证正确也应拒绝)→ 成功操作 Reset 清零。与家族原语的边界:KeyedLimiter 是速率型令牌桶(表达不了 「计数达标后进入持续拒绝态」);breaker 是调用方视角的实例级熔断(无 key 隔离); budget 是超限记账存档(无拒绝态与 Reset)。
语义细则:
- 失败样本滑动窗口计数,窗口外自然老化——锁定到期后若旧失败仍在窗口内, 首个新失败即重新锁定(重复违规者语义,对齐 fail2ban findtime 行为);
- 锁定期间新失败只入窗口记账(封顶 Threshold 个),不延长当前锁定;
- 容量护栏 fail-open:表满且 key 未登记时丢弃失败样本并计入 OverCapHits;
- 锁定中的条目免于空闲回收(提前驱逐等于提前解锁)。
典型用法(登录防爆破):
lo := ratelimit.NewLockout(ratelimit.LockoutConfig{
Window: 10 * time.Minute, Threshold: 5, LockDuration: 15 * time.Minute,
MaxEntries: 10_000,
})
if locked, remain := lo.CheckLocked("user:42"); locked {
return &LockedError{RemainingSeconds: int(remain.Seconds())} // 对外 429
}
if err := verifyCredential(); err != nil {
if locked, _ := lo.RecordFailure("user:42"); locked { ... }
return err
}
lo.Reset("user:42") // 登录成功
func NewLockoutOf ¶ added in v0.11.0
func NewLockoutOf[K comparable](cfg LockoutConfig) *LockoutOf[K]
NewLockoutOf 创建任意 comparable key 的防爆破锁(IP、用户名、API Key 等)。
func (*LockoutOf[K]) CheckLocked ¶ added in v0.11.0
CheckLocked 查询 key 是否处于锁定拒绝态:locked=true 时 lockRemain 为剩余 锁定时长(供对外 429 文案),未锁定或未登记返回 (false, 0)。查询刷新空闲计时。 返回序与 RecordFailure 统一为 (locked, lockRemain)——两个方法同序是刻意设计, 避免调用方在相邻调用间交换返回值顺序。
func (*LockoutOf[K]) Close ¶ added in v0.11.0
func (l *LockoutOf[K]) Close()
Close 停止后台清扫 goroutine。可安全重复调用(KeyedLimiterOf 同款幂等语义)。
func (*LockoutOf[K]) OverCapHits ¶ added in v0.11.0
OverCapHits 返回容量护栏丢弃失败样本的累计次数(可观测性)。
func (*LockoutOf[K]) RecordFailure ¶ added in v0.11.0
RecordFailure 记一次失败事件。
- 触发锁定或 key 已处于锁定态:返回 (true, 剩余锁定时长);
- 未触发:返回 (false, 0);
- 容量护栏拒绝(表满且 key 未登记):失败样本被丢弃,返回 (false, 0), 可经 OverCapHits 观测。
锁定期间调用只入窗口记账,不延长当前锁定。
type SlidingWindow ¶
type SlidingWindow struct {
// contains filtered or unexported fields
}
SlidingWindow 是滑动窗口限流器。 在指定时间窗口内限制请求次数。
func NewSlidingWindow ¶
func NewSlidingWindow(window time.Duration, maxCount int) *SlidingWindow
NewSlidingWindow 创建滑动窗口限流器。
参数:
- window: 时间窗口大小
- maxCount: 窗口内最大请求数
用法:
limiter := ratelimit.NewSlidingWindow(time.Minute, 100) // 每分钟最多 100 次
func (*SlidingWindow) AllowN ¶
func (sw *SlidingWindow) AllowN(n int) bool
AllowN 检查是否允许 N 个请求。n<=0 视为 0 个请求,直接放行(不占槽位)。
func (*SlidingWindow) Reserve ¶
func (sw *SlidingWindow) Reserve() time.Duration
Reserve 预留一个请求,返回需要等待的时间。 返回 0 表示立即放行(槽位已计入窗口);返回 d>0 表示需等待 d 后放行, 槽位已按 now+d 预占——与 x/time/rate 的 Reserve 语义一致:预留即消费, 并发调用者拿到的是互不相同的槽位而非同一个"虚拟许可"。
type TokenBucket ¶
type TokenBucket struct {
// contains filtered or unexported fields
}
TokenBucket 是令牌桶限流器。 基于 golang.org/x/time/rate 实现,支持平滑限流;时间源可注入 WithTokenClock (缺省 time.Now),确定性测试注入假时钟即可无 sleep 驱动配额恢复与 Retry-After 探测(#24)。
func NewTokenBucket ¶
func NewTokenBucket(rps rate.Limit, burst int, opts ...TokenBucketOption) *TokenBucket
NewTokenBucket 创建令牌桶限流器。
参数:
- rps: 每秒允许的请求数(Requests Per Second)
- burst: 突发容量(令牌桶最大容量)
用法:
limiter := ratelimit.NewTokenBucket(100, 200) // 100 QPS, burst 200
func (*TokenBucket) Reserve ¶
func (tb *TokenBucket) Reserve() time.Duration
Reserve 预留一个令牌,返回需要等待的时间。
func (*TokenBucket) RetryAfter ¶ added in v0.5.0
func (tb *TokenBucket) RetryAfter() (time.Duration, bool)
RetryAfter 探测下一个可用令牌的等待时长,不消耗令牌(Reserve→Delay→Cancel, 归还按同一注入时钟结算)。ok=false 表示限流器本身不可用(如 burst=0 且 rps=0)。 被拒请求不会自我消耗配额——429 + Retry-After 场景的标准姿势。
type TokenBucketOption ¶ added in v0.12.0
type TokenBucketOption func(*TokenBucket)
TokenBucketOption 用于定制 TokenBucket(函数选项模式;按本仓命名硬规则为 XxxOption,禁止裸 Option)。
func WithTokenClock ¶ added in v0.12.0
func WithTokenClock(clock func() time.Time) TokenBucketOption
WithTokenClock 注入时钟(缺省 time.Now;nil 视为缺省)。放行判定、预留与 Retry-After 探测全部走该时钟——假时钟下「1 秒后应恢复配额」「Retry-After 精确回填」均可确定性地断言,无 sleep 无 flake。