0%

go 漏桶

相比于令牌桶,漏桶更严格,调用方只能严格按照预定的间隔顺序进行消费调用.
uber 在 Github 上开源了一套用于服务限流的 go 语言库 ratelimit, 该组件基于 Leaky Bucket(漏桶) 实现。

ratelimit使用

官方 example

1
2
3
4
5
6
7
8
rl := ratelimit.New(100) // per second

prev := time.Now()
for i := 0; i < 10; i++ {
now := rl.Take()
fmt.Println(i, now.Sub(prev))
prev = now
}

ratelimit 实现

根据传入的limit参数,计算每个请求的间隔
limiter.perRequest = time.Second / time.Duration(rate)
根据最近的 limiter.last最近获取时间,当前时间 now - last = interval 为两次请求的间隔,如果间隔小于 perRequest,那么sleep
perRequest - interval 时间即可.

1
2
3
4
5
6
7
8
newState.sleepFor += t.perRequest - now.Sub(oldState.last)
sleepFor = t.perRequest - now.Sub(t.last)
if sleepFor > 0 {
t.clock.Sleep(sleepFor)
t.last = now.Add(sleepFor)
} else {
t.last = now
}

最大松弛量

在 uber-go 实现的 ratelimit 中,可以把之前间隔比较长的请求的时间,匀给后面的使用,保证每秒请求数 (RPS) 即可。

1
2
3
4
5
6
7
8
9
// 为了防止无限等待达不到限流的效果,引入 maxSlack
if newState.sleepFor < t.maxSlack {
newState.sleepFor = t.maxSlack
}
// 将多余的时间给后面的请求使用
if newState.sleepFor > 0 {
newState.last = newState.last.Add(newState.sleepFor)
interval, newState.sleepFor = newState.sleepFor, 0
}

对于需要严格限制请求间隔的情况,ratelimit 提供 WithoutSlack option function,不允许最大松弛量

对于需要使用自定义时钟的情况,提供 WithClock option function,只要实现以下接口即可.

1
2
3
4
type Clock interface {
Now() time.Time
Sleep(time.Duration)
}

参考资料