接口限流

2026年1月18日 · 145 字 · 1 分钟

简单介绍令牌桶算法和golang官方的接口限流库

接口限流

令牌桶算法

r:每秒钟向通内放入r个令牌,即每隔1/r秒放一个令牌

b:桶的最大容量是b,桶满后试图再放入的令牌将会丢弃掉

当有人请求n个令牌的时候,如果桶中的令牌数小于n则请求方阻塞或者直接放弃,否则直接取走n个令牌

例子

假如r=10,b=5

那么每隔0.1秒产生一个令牌,则0.5秒桶就满了,之后生成就装不下去了,加入再0.7秒到0.8秒之间,有5个人想请求1个令牌,全部都可以顺利取走

那么加入0.9秒一直在生产令牌,那么还是只有5个令牌,0.9秒到1秒取走一个,那么1秒内就有处理了一个请求

场景

秒杀场景:如果有10个商品,那么就可以设置令牌的初始为10,谁手快就把令牌抢走了,如果谁不付款,就把令牌归还,谁再去抢

限制qps

golang官方的限流器

golang.org/x/time/rate 是 Go 官方提供的令牌桶算法限流器实现,被广泛应用于 Kubernetes 等大型开源项目中。

核心概念

  • Limit: 事件发生的最大频率(每秒事件数),类型为 float64
  • Limiter: 限流器核心结构,控制事件频率
  • b (burst): 令牌桶的最大容量,允许突发流量

快速使用

import "golang.org/x/time/rate"

// 创建一个限流器: 每秒10个请求,突发容量100
limiter := rate.NewLimiter(rate.Limit(10), 100)

核心方法

1. Allow / AllowN - 非阻塞检查

// 检查是否允许当前请求通过(不阻塞)
if limiter.Allow() {
    // 处理请求
}

// 检查是否允许n个请求通过
if limiter.AllowN(time.Now(), 5) {
    // 处理5个请求
}

Allow 返回 true 表示有足够令牌,调用方可继续执行;返回 false 表示被限流。

2. Reserve / ReserveN - 预约令牌

// 预约一个令牌用于未来使用
r := limiter.Reserve()
if !r.OK() {
    // 无法获取令牌
}
// 等待delay后使用令牌
time.Sleep(r.Delay())
// 执行操作...

// 可以取消预约
r.Cancel()

适用于需要预知等待时间并做出决策的场景。

3. Wait / WaitN - 阻塞等待(推荐)

// 等待获取1个令牌(阻塞,直到获取到或上下文取消)
err := limiter.Wait(ctx)

// 等待获取n个令牌
err := limiter.WaitN(ctx, 5)

这是最常用的方法,会阻塞直到获取到所需令牌或上下文超时。

动态调整限流

// 动态调整速率(从当前时间开始)
limiter.SetLimit(rate.Limit(20))

// 动态调整桶容量
limiter.SetBurst(200)

// 在指定时间点调整
limiter.SetLimitAt(time.Now(), rate.Limit(15))
limiter.SetBurstAt(time.Now(), 100)

获取限流器状态

// 获取当前速率限制
fmt.Println("Rate:", limiter.Limit())   // 如: 10

// 获取当前突发容量
fmt.Println("Burst:", limiter.Burst())  // 如: 100

// 获取当前可用令牌数
fmt.Println("Tokens:", limiter.Tokens())