【GoCN酷Go推荐】单机限流器time/rate库

1、限流是什么

顾名思义,限流就是只在一段时间内对流通量的管控限制。限流器是后台服务中的非常要的组件,用于限制请求速率,保护服务,避免服务过载。

golang标准库中自带了令牌桶限流算法的实现golang.org/x/time/rate

2、如何安装

使用前要安装它,安装要在当前项目所在目录中执行 go get -u golang.org/x/time/rate 即可。

3、什么是限流器

漏桶限流器

漏桶算法是比较常见的限流算法,其原理就如漏斗一样,进水口是请求流,出水口是服务流,如果请求流量过大,则会导致溢出从而拒绝请求。

在Go语言中有一种数据结构可以十分贴切的模拟漏桶,那就是chan。

 //桶的容量为Max
 ch := make(chan *Request, Max)

 //流量进入
 if(len(ch)<Max){
  ch <- req //桶未满
 }else {
  cancel(req) //桶满,拒绝请求
 }

 //流量处理 预设处理间隔 m
 for {
  time.Sleep(m * time.Second)
  r := <- ch
  Handle(r)
 }

这是最简单的漏桶实现的代码。

当桶没有积水时,即入桶速率更小,此时以入桶速率处理请求。当桶中有积水时,即入桶速率更大,此时以预设出桶速率处理请求。当桶满时,丢弃入桶请求。

令牌桶限流器

令牌桶的工作流程则分为三个阶段:生产令牌,判定是否处理,消耗令牌。

生产令牌:系统会以一定的频率向桶中放入令牌,桶满时丢弃令牌。

判定是否处理:当请求到来时,判断桶中的令牌是否足够,不足够时是否等待,是否丢弃。

消耗令牌:令牌足够时,减少桶中的令牌并处理请求。

4、使用令牌桶限流器

我们依旧可以通过channel模拟的方式来写令牌桶的算法,具体实现类似漏桶的倒置。然而golang的标准库自带的库在没有使用chan的情况下完成了高效的令牌桶算法。

导入库

 import "golang.org/x/time/rate"

创建令牌桶对象

 // r(float64)代表每秒向桶中放入多少令牌
 // b(int)代表 桶 的容量
 limiter := rate.NewLimiter(r,b)

令牌桶方法

 func (lim *limter) Wait(ctx context.Context)(err error) //等同于WaitN(ctx,1)
 func (lim *limter) WaitN(ctx context.Context,n int)(err error)

 func (lim *limter) Allow()(bool) //等同于Allow(time.Now(),1)
 func (lim *limter) AllowN(now time.Time, n int)(bool)


 func (lim *limter) Reserve() *Reservation //等同于ReserveN(time.Now(),1)
 func (lim *limter) ReserveN(now time.Time,n int) *Reservation

WaitN方法会在令牌数量满足时返回,当不满足时会阻塞一段时间,传入的ctx参数可以设定最长等待时间。

AllowN方法则会判断截止now时刻,桶中的令牌是否足够n,足够时则消费令牌返回true,不足时直接返回false,不消费令牌。

ReserveN方法返回一个结构体指针用于描述针对时刻now和需求令牌n的反馈,其Delay()方法返回需等待时间。Cancel方法会将获取的令牌返回。

5、time/rate 源码分析

先介绍一下Limter结构体

type Limiter struct {
 mu     sync.Mutex
 limit  Limit
 burst  int
 tokens float64
 // last is the last time the limiter's tokens field was updated
 last time.Time
 // lastEvent is the latest time of a rate-limited event (past or future)
 lastEvent time.Time
}

从结构体中,我们可以很清楚的发现,在令牌桶的代码中没有chan的变量。

源码中采用时间参与计算的方式来模拟令牌桶。

  • mu Mutex用于在重新设定limit和burst时保证异步安全

  • limit 初始化时传入,表示放入令牌的频率

  • burst 令牌桶容量,允许突发高流量请求

  • last 上一次令牌数量更新的时间

  • lastEvent 上一次请求事件的时间

下面我们另一个重要的结构Reservation

type Reservation struct {
 ok        bool
 lim       *Limiter
 tokens    int
 timeToAct time.Time
 // This is the Limit at reservation time, it can change later.
 limit Limit
} 
func (lim *Limiter) reserveN(now time.Time,n int, maxFutureReserve time.Duration) Reservation {...}

Reservation描述了一次请求的结果(消耗令牌,取消时返还)

  • ok 表示令牌是否满足

  • lim Limiter指针,用于取消时归还令牌

  • tokens 模拟时令牌桶内的令牌数量

  • timeToAct 模拟的时间

  • limit 模拟时的令牌放入频率

reserveN函数是对当前请求的一次模拟,Reservationf反映模拟的结果。常用的六个方法Wait/WaitN,Allow/AllowN,Reserve/ReserveN都通过调用reserveN函数进行模拟,然后各自处理模拟结果。

  • Allow/AllowN直接返回ok值。

  • Reserve/ReserveN直接返回此模拟结果。

  • Allow/AllowN通过Reserve提供的信息进行阻塞,并在超时时调用Cancel返还令牌。

总结

相比于漏桶,令牌桶最大的优势在于可以接受一定量的流量洪峰(根据桶的大小),其原理在于当请求速率小于预设值时,令牌桶积累令牌,当请求速率大于预设值时,令牌桶额外消耗令牌。

对于限流器的实现,标准库采用数学模拟的方式来避免chan和Goroutine的肆意创建运行,这样可以避免限流器的滥用对协程管理以及GC所带来的压力。

缺点

无论是漏桶还是令牌桶,说到底都是单机限流器,其缺点在于创建限流器的时候要设定阈值,在微服务集群中很难预见性的去给予合适的阈值来保证服务的稳定。

实际应用

在使用令牌桶限流器时,动态调整令牌桶的放入频率和桶的大小,其依据可以是令牌桶在运行时的被访问频率,也可以是CPU的使用率等等。

参考资料

  • Golang限流器time/rate使用介绍(https://zhuanlan.zhihu.com/p/89820414)

还想了解更多吗?

更多请查看:https://github.com/Shopify/sarama

欢迎加入我们GOLANG中国社区:https://gocn.vip/

《酷Go推荐》招募:

各位Gopher同学,最近我们社区打算推出一个类似GoCN每日新闻的新栏目《酷Go推荐》,主要是每周推荐一个库或者好的项目,然后写一点这个库使用方法或者优点之类的,这样可以真正的帮助到大家能够学习到

新的库,并且知道怎么用。

大概规则和每日新闻类似,如果报名人多的话每个人一个月轮到一次,欢迎大家报名!(报名地址:https://wj.qq.com/s2/7734329/3f51)

扫码也可以加入 GoCN 的大家族哟~

 距离 Gopher China 2021大会还有最后一天!

抓紧机会!点击下方「阅读原文」报名参加大会!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值