在go中的使用
Go
提供了一个package(golang.org/x/time/rate),采用令牌桶的算法实现,用来方便的对速度进行限制。
type Limiter struct {
limit Limit
burst int
mu sync.Mutex
tokens float64
last time.Time
lastEvent time.Time
}
func NewLimiter(r Limit, b int) *Limiter
首先创建一个rate.Limiter
,其有两个参数,第一个参数为允许每秒发生多少次事件,第二个参数是其缓存最大可存多少个事件。这个桶一开始容量为b,装满b个token,然后每秒往里面填充r个token。由于令牌桶中最多有b个token,所以一次最多只能允许b个事件发生,一个事件花费掉一个token。
rate.Limiter
提供三种主要的函数。
Wait/WaitN
func (lim *Limiter) Wait(ctx context.Context) (err error)
func (lim *Limiter) WaitN(ctx context.Context, n int) (err error)
Wait
是WaitN(ctx, 1)
的简化形式。WaitN
阻塞当前直到lim允许n个事件的发生。当没有可用或足够的事件时,将阻塞等待,推荐实际程序中使用这个方法。
Allow/AllowN
func (lim *Limiter) Allow() bool
func (lim *Limiter) AllowN(now time.Time, n int) bool
Allow
是函数AllowN(time.Now(), 1)
的简化函数。AllowN
标识在时间now
的时候,n个事件是否可以同时发生(也意思就是now的时候是否可以从令牌桶中取n个token)。适合在超出频率的时候丢弃或跳过事件的场景。
Reserve/ReserveN
func (lim *Limiter) Reserve() *Reservation
func (lim *Limiter) ReserveN(now time.Time, n int) *Reservation
Reserve
是ReserveN(time.Now(), 1)
的简化形式。ReserveN
返回对象Reservation
,用于标识调用者需要等多久才能等到n个事件发生(意思就是等多久令牌桶中至少含有n个token)。Wait/WaitN
和Allow/AllowN
其实就是基于其之上实现的,通过sleep等待时间和直接返回状态。如果想对事件发生的频率和等待处理逻辑更加精细的话就可以使用它。
动态调整速率
Limiter支持可以调整速率和桶大小:
SetLimit(Limit)
改变放入Token的速率
SetBurst(int)
改变Token桶大小
有了这两个方法,可以根据现有环境和条件,根据我们的需求,动态的改变Token桶大小和速率
example
package main
import (
"context"
"fmt"
"time"
"golang.org/x/time/rate"
)
func main() {
l := rate.NewLimiter(2, 5)
ctx := context.Background()
start := time.Now()
// 要处理二十个事件
for i := 0; i < 20; i++ {
l.Wait(ctx)
// dosomething
}
fmt.Println(time.Since(start)) // output: 7.501262697s (初始桶内5个和每秒2个token)
}
最终花费时间为7.5s,即(20-5)/2
网友评论