限流器是后台服务中十分重要的组件,在实际的业务场景中使用居多,其设计在微服务、网关、和一些后台服务中会经常遇到。限流器的作用是用来限制其请求的速率,保护后台响应服务,以免服务过载导致服务不可用现象出现。
限流器的实现方法有很多种,例如 Token Bucket、滑动窗口法、Leaky Bucket等。
golang.org/x/time/rate
参考:go语言中文文档:www.topgoer.com
转自:https://segmentfault.com/a/1190000023824362
令牌桶算法令牌桶设计比较简单,可以简单的理解成一个只能存放固定数量雪糕!的一个冰箱,每个请求可以理解成来拿雪糕的人,有且只能每一次请求拿一块,那雪糕拿完了会怎么样呢?这里会有一个固定放雪糕的工人,并且他往冰箱里放雪糕的频率都是一致的,例如他 1s 中只能往冰箱里放 10 块雪糕,这里就可以看出请求响应的频率了。
令牌桶设计概念:
令牌:每次请求只有拿到 Token 令牌后,才可以继续访问;
桶:具有固定数量的桶,每个桶中最多只能放设计好的固定数量的令牌;
入桶频率:按照固定的频率往桶中放入令牌,放入令牌不能超过桶的容量。
也就是说,基于令牌桶设计算法就限制了请求的速率,达到请求响应可控的目的,特别是针对于高并发场景中突发流量请求的现象,后台就可以轻松应对请求了,因为到后端具体服务的时候突发流量请求已经经过了限流了。
具体设计限流器定义limit、burst 和 token 是这个限流器中核心的参数,请求并发的大小在这里实现的。
在令牌发放之后,会存储在 Reservation 预约对象中:
消费 Token
Limiter 提供了三类方法供用户消费 Token,用户可以每次消费一个 Token,也可以一次性消费多个 Token。而每种方法代表了当 Token 不足时,各自不同的对应手段。
Wait、WaitNfunc (lim *Limiter) Wait(ctx context.Context) (err error)
func (lim *Limiter) WaitN(ctx context.Context, n int) (err error)
WaitN(ctx, 1)
使用 Wait 方法消费 Token 时,如果此时桶内 Token 数组不足 ( 小于 n ),那么 Wait 方法将会阻塞一段时间,直至 Token 满足条件。如果充足则直接返回。
Allow、AllowNfunc (lim *Limiter) Allow() bool
func (lim *Limiter) AllowN(now time.Time, n int) bool
AllowN 方法表示,截止到当前某一时刻,目前桶中数目是否至少为 n 个,满足则返回 true,同时从桶中消费 n 个 token。
反之返回不消费 Token,false。
通常对应这样的线上场景,如果请求速率过快,就直接丢到某些请求。
Reserve、ReserveN官方提供的限流器有阻塞等待式的 Wait,也有直接判断方式的 Allow,还有提供了自己维护预留式的,但核心的实现都是下面的 reserveN 方法。
func (lim *Limiter) Reserve() *Reservation
func (lim *Limiter) ReserveN(now time.Time, n int) *Reservation
Reservation *
Delay()
Cancel()
具体使用
rate 包中提供了对限流器的使用,只需要指定 limit(放入桶中的频率)、burst(桶的大小)。
在这里,我把桶设置成了每一毫秒投放一次令牌,桶容量大小为 10,起一个 http 的服务,模拟后台 API。
接下来做一个压力测试,看看效果如何:
在这里,可以看到,当使用 AllowN 方法中,只有当令牌 Token 生产出来,才可以消费令牌,继续请求,剩余的则是将其请求抛弃,当然在实际的业务处理中,可以用比较友好的方式反馈给前端。
在这里,先有的几次请求都会成功,是因为服务启动后,令牌桶会初始化,将令牌放入到桶中,但是随着突发流量的请求,令牌按照预定的速率生产令牌,就会出现明显的令牌供不应求的现象。
开源文化目前 time/rate 是一个独立的限流器开源解决方案,感兴趣的小伙伴可以给此项目一个 Star,谢谢。
GitHub
golang/time
限流器系列(2) — Token Bucket 令牌桶
Golang 限流器的使用和实现
Golang 标准库限流器 time/rate 使用介绍
https://github.com/golang/time/rate.go