背景
常见限流算法对比
| 精确性 | 输出抖动 | 瞬间高峰流量 | 性能 | ||
|---|---|---|---|---|---|
| 单窗口 | 不足 | 频繁 | 支持 | 高 | |
| 滑动窗口 | 窗口越多越精确 | 频繁 | 支持 | 高 | |
| 漏斗 | 高,取决于桶数量 | 无 | 不支持 | 请求排队导致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,
}
}
标准库中提供的构造方法,入参有两个:
- 第一个参数
r Limit,表示每秒钟Token Bucket中会产生多少token,Limit类型是float64的override。 - 第二个参数
b int,表示在初始状态下,桶内的token数。
那么当我们想创建一个初始大小为10,之后每秒钟生产5个token的Token Bucket,可以用如下方式:
limiter :=rate.NewLimiter(5, 10)
特殊情况
rate.Every
time/rate除了构造函数,还提供了Every方法。例如:
表示创建一个每秒钟产生一个token的Token Bucket。limiter := rate.Every(time.Second)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)
- Wait(ctx)等价于WaitN(ctx, 1)
- 当Token Bucket中的token不满足消费条件的时候(小于N),该方法将会阻塞,直到条件满足或超时。
- 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)
- 当Token Bucket中的token不满足消费条件的时候(小于N),该方法将返回false,否则返回true。
- 生产场景下,如果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)
- 调用后无论是否满足消费条件,都会返回一个
*Reservation对象。 - 该方法判断limit是否能在规定时间提供N个token。
Reservatio.OK()返回false时,表示无法计算等待时长,直接进入等待,可以调用Cancel()取消此次操作并归还使用的token。Reservatio.OK()返回true时,表示需要等待一段时间才能获取token,通过Delay()方法返回需要等待的时间。
Dynamic Adjustment
func (lim *Limiter) SetLimit(newLimit Limit)
该方法可以动态改变放入token的速率