Go标准库限流器time/rate|Go主题月

2,048 阅读2分钟

背景

常见限流算法对比

精确性输出抖动瞬间高峰流量性能
单窗口不足频繁支持
滑动窗口窗口越多越精确频繁支持
漏斗高,取决于桶数量不支持请求排队导致RT增加
令牌桶高,取决于桶数量支持瞬间高峰流量,除此之外无抖动支持

Golang 标准库中自带了基于Token Bucket(令牌桶)的限流算法的实现。

Construct

// NewLimiter returns a new Limiter that allows events up to rate r and permits
// bursts of at most b tokens.
func NewLimiter(r Limit, b int) *Limiter {
	return &Limiter{
		limit: r,
		burst: b,
	}
}

标准库中提供的构造方法,入参有两个:

  1. 第一个参数r Limit,表示每秒钟Token Bucket中会产生多少token,Limit类型是float64的override。
  2. 第二个参数b int,表示在初始状态下,桶内的token数。

那么当我们想创建一个初始大小为10,之后每秒钟生产5个token的Token Bucket,可以用如下方式:

limiter :=rate.NewLimiter(5, 10)

特殊情况

  • rate.Every
    time/rate除了构造函数,还提供了Every方法。例如:
    limiter := rate.Every(time.Second)
    
    表示创建一个每秒钟产生一个token的Token Bucket。
  • b == 0
    允许声明容量为0的Token Bucket,这种情况下将拒绝所有的请求。
  • Inf
    time/rate包中还声明了一个Inf常量,该常量定义如下:
    // Inf is the infinite rate limit; it allows all events (even if burst is zero).
    const Inf = Limit(math.MaxFloat64)
    
    b== 0 的情况相反。根据定义,如果参数Limit为Inf时,将会允许所有的请求,即使b== 0。

Wait/WaitN

func (lim *Limiter) Wait(ctx context.Context) (err error)
func (lim *Limiter) WaitN(ctx context.Context, n int) (err error)
  1. Wait(ctx)等价于WaitN(ctx, 1)
  2. 当Token Bucket中的token不满足消费条件的时候(小于N),该方法将会阻塞,直到条件满足或超时。
  3. Wait方法可以通过context的类型(DeadLine,TimeOut),例如来控制等待时长。
     ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
     defer cancel()
     err := limiter.WaitN(ctx, 2)
    

Allow/AllowN

func (lim *Limiter) Allow() bool
func (lim *Limiter) AllowN(now time.Time, n int) bool

Allow(time.Now())等价于AllowN(time.Now(), 1)

  1. 当Token Bucket中的token不满足消费条件的时候(小于N),该方法将返回false,否则返回true。
  2. 生产场景下,如果rate过高,会直接丢失该请求。

Reserve/ResrveN

func (lim *Limiter) Reserve() *Reservation
func (lim *Limiter) ReserveN(now time.Time, n int) *Reservation

Reserve(time.Now())等价于ResrveN(time.Now(), 1)

  1. 调用后无论是否满足消费条件,都会返回一个*Reservation对象。
  2. 该方法判断limit是否能在规定时间提供N个token。
  3. Reservatio.OK()返回false时,表示无法计算等待时长,直接进入等待,可以调用Cancel()取消此次操作并归还使用的token。
  4. Reservatio.OK()返回true时,表示需要等待一段时间才能获取token,通过Delay()方法返回需要等待的时间。

Dynamic Adjustment

func (lim *Limiter) SetLimit(newLimit Limit) 

该方法可以动态改变放入token的速率