前言

Golang 官方提供的扩展库里就自带了限流算法的实现,即 golang.org/x/time/rate。该限流器也是基于 Token Bucket(令牌桶) 实现的。

限流器的内部结构

time/rate 包的 Limiter 类型对限流器进行了定义,所有限流功能都是通过基于 Limiter 类型实现的,其内部结构如下

type Limiter struct {
mu sync.Mutex
limit Limit
burst int // 令牌通的大小
tokens float64
last time.Time // 上次更新tokens的时间
lastEvent time.Time // 上次发生限速事件的时间(通过或者限制都是限速器事件)
}

字段的作用:

  • limit: limit 字段表示往桶里放Token的速率,它的类型是Limit,是int64的类型别名。设置 limit 时既可以用数字指定每秒向桶中放多少个Token,也可以指定向桶中放入Token的时间间隔,其实指定了每秒放Token的个数后就能计算出放每个Token的时间间隔了。
  • burst: 令牌桶的大小。
  • tokens:桶中的令牌。
  • last:上次往桶中放 Token 的时间。
  • lastEvent:上次发生限速器事件的时间(通过或者限制都是限速器事件)

可以看到在 timer/rate 的限流器实现中,并没有单独维护一个 Timer 和队列去真的每隔一段时间向桶中放令牌,而是仅仅通过计数的方式表示桶中剩余的令牌。每次消费取 Token 之前会先根据上次更新令牌数的时间差更新桶中Token数。

构造限流器

构造一个限流器对象

limiter := rate.NewLimiter(10, 100)

这里有两个参数:

  1. 第一个参数是 r Limit,设置的是限流器Limiter的limit字段,代表每秒可以向 Token 桶中产生多少 token。Limit 实际上是 float64 的别名。
  2. 第二个参数是 b int,b 代表 Token 桶的容量大小,也就是设置的限流器 Limiter 的burst字段。

对于以上例子来说,其构造出的限流器的令牌桶大小为 100, 以每秒 10 个 Token 的速率向桶中放置 Token。

除了给r Limit参数直接指定每秒产生的 Token 个数外,还可以用 Every 方法来指定向桶中放置 Token 的间隔,例如:

limit := rate.Every(100 * time.Millisecond)
limiter := rate.NewLimiter(limit, 100)

以上就表示每 100ms 往桶中放一个 Token。本质上也是一秒钟往桶里放 10 个。

使用限流器

Limiter 提供了三类方法供程序消费 Token,可以每次消费一个 Token,也可以一次性消费多个 Token。每种方法代表了当 Token 不足时,各自不同的对应手段,可以阻塞等待桶中Token补充,也可以直接返回取Token失败。

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)

当使用 Wait 方法消费 Token 时,如果此时桶内 Token 数组不足 (小于 N),那么 Wait 方法将会阻塞一段时间,直至 Token 满足条件。如果充足则直接返回。

这里可以看到,Wait 方法有一个 context 参数。我们可以设置 context 的 Deadline 或者 Timeout,来决定此次 Wait 的最长时间。

// 一直等到获取到桶中的令牌
err := limiter.Wait(context.Background())
if err != nil {
fmt.Println("Error: ", err)
} // 设置一秒的等待超时时间
ctx, _ := context.WithTimeout(context.Background(), time.Second * 1)
err := limiter.Wait(ctx)
if err != nil {
fmt.Println("Error: ", err)
}

Allow/AllowN

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

Allow 实际上就是对 AllowN(time.Now(), 1) 进行简化的函数。

AllowN 方法表示,截止到某一时刻,目前桶中数目是否至少为 n 个,满足则返回 true,同时从桶中消费 n 个 token。反之不消费桶中的Token,返回false。

对应线上的使用场景是,如果请求速率超过限制,就直接丢弃超频后的请求。

if limiter.AllowN(time.Now(), 2) {
fmt.Println("event allowed")
} else {
fmt.Println("event not allowed")
}

Reserve/ReserveN

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

Reserve 相当于 ReserveN(time.Now(), 1)

ReserveN 的用法就相对来说复杂一些,当调用完成后,无论 Token 是否充足,都会返回一个 *Reservation 对象。你可以调用该对象的 Delay() 方法,该方法返回的参数类型为 time.Duration,反映了需要等待的时间,必须等到等待时间之后,才能进行接下来的工作。如果不想等待,可以调用 Cancel() 方法,该方法会将 Token 归还。

举一个简单的例子,我们可以这么使用 Reserve 方法。

r := limiter.Reserve()
f !r.OK() {
// Not allowed to act! Did you remember to set lim.burst to be > 0 ?
return
}
time.Sleep(r.Delay())
Act() // 执行相关逻辑

动态调整速率和桶大小

Limiter 支持创建后动态调整速率和桶大小:

  1. SetLimit(Limit) 改变放入 Token 的速率
  2. SetBurst(int) 改变 Token 桶大小

有了这两个方法,可以根据现有环境和条件以及我们的需求,动态地改变 Token 桶大小和速率。

总结

今天总结了 Golang 官方限流器的使用方法,它是一种令牌桶算实现的限流器。其中 Wait/WaitN,Allow/AllowN 这两组方法在平时用的比较多,前者是消费Token时如果桶中Token不足可以让程序等待桶中新Token的放入(最好设置上等待时长)后者则是在桶中的Token不足时选择直接丢弃请求。

除了Golang官方提供的限流器实现,Uber公司开源的限流器uber-go/ratelimit也是一个很好的选择,与Golang官方限流器不同的是Uber的限流器是通过漏桶算法实现的,不过对传统的漏桶算法进行了改良。

go 限流器 rate的更多相关文章

  1. go的令牌桶实现库 go-rate

    关于我 我的博客|文章首发 go-rate是速率限制器库,基于 Token Bucket(令牌桶)算法实现. go-rate被用在LangTrend的生产中 用于遵守GitHub API速率限制. 速 ...

  2. python分布式环境下的限流器

    项目中用到了限流,受限于一些实现方式上的东西,手撕了一个简单的服务端限流器. 服务端限流和客户端限流的区别,简单来说就是: 1)服务端限流 对接口请求进行限流,限制的是单位时间内请求的数量,目的是通过 ...

  3. go语言实现限流器

    本文:https://chai2010.cn/advanced-go-programming-book/ch5-web/ch5-06-ratelimit.html Ratelimit 服务流量限制 计 ...

  4. guava限流器RateLimiter原理及源码分析

    前言 RateLimiter是基于令牌桶算法实现的一个多线程限流器,它可以将请求均匀的进行处理,当然他并不是一个分布式限流器,只是对单机进行限流.它可以应用在定时拉取接口数据, 预防单机过大流量使用. ...

  5. Java技术开发专题系列之【Guava RateLimiter】针对于限流器的入门到精通(针对于源码分析介绍)

    Guava包中限流实现分析 RateLimiter 之前的文章中已经介绍了常用的限流算法,而google在Java领域中使用Guava包中的限流工具进行服务限流. 回顾使用案例 Google开源工具包 ...

  6. get back to the slower clock rate that allows it to save more power

    http://www.howtogeek.com/177790/why-you-cant-use-cpu-clock-speed-to-compare-computer-performance/ Wh ...

  7. Clock rate

    https://en.wikipedia.org/wiki/Clock_rate The clock rate typically refers to the frequency at which a ...

  8. [ZZ] Equal Error Rate (EER)

    这篇博客很全面 http://www.cnblogs.com/cdeng/p/3471527.html ROC曲线 1.混淆矩阵(confusion matrix) 针对预测值和真实值之间的关系,我们 ...

  9. [蓝牙] 4、Heart Rate Service module

    Detailed Description Heart Rate Service module. This module implements the Heart Rate Service with t ...

  10. False Discovery Rate, a intuitive explanation

    [转载请注明出处]http://www.cnblogs.com/mashiqi Today let's talk about a intuitive explanation of Benjamini- ...

随机推荐

  1. 禁止所有搜索爬虫访问网站指定目录robots.txt

    禁止所有搜索爬虫访问网站指定目录 用自己私人的服务器来测试吧,99买阿里云 文件放置不让爬取的网站文件夹目录里 robots.txt User-agent: * Disallow: / User-Ag ...

  2. Qt音视频开发14-音视频文件保存基类的设计

    一.前言 视频综合应用示例,包括了多种内核,在保存文件这个功能上,需要一个通用的文件保存基类AbstractSaveThread,这个基类定义了是否打印线程消息标志位.直接写入还是排队写入标志位.文件 ...

  3. Qt开源作品18-无边框背景透明窗体

    一.前言 用Qt来做无边框北京透明窗体非常简单,根本不需要用什么系统层的API来实现透明什么的,Qt本身提供了很多种设置窗体透明的方法,除了可以设置窗体的属性为透明以外,还可以设置透明度函数,以及qs ...

  4. DataTable 循环取值

    //list是datatable类型 for (int i = 0; i < list.Rows.Count; i++) { var A = list.Rows[i]["列名" ...

  5. ESLint is disabled since its execution has not been approved or denied yet

    VS Code 装好ESLint 插件报黄线的问题,具体解决方法如下所示: ESLint is disabled since its execution has not been approved o ...

  6. VEP注释文件下载网址

    Ensembl提供多个物种的注释文件: ftp://ftp.ensembl.org/pub/release-96/variation/VEP

  7. 冷水机超频AMD 2600X 4.5G

    整个测试大概持续了一个半小时,期间水温保持在2度左右(±1摄氏度).后来开始冷凝,而且超频也到了4.5G瓶颈,所以停了下来. 全核4.4G过R154.5G过CPU-Z认证(点击查看)温度表现,一般吧. ...

  8. 【源码】ByteToMessageDecoder对比自定义实现

    前言 在上一篇随笔中,我们探讨了如何实现一套自定义通信协议,其中涉及到的粘包和拆包处理最初是完全自定义实现的,后来则改为了继承 ByteToMessageDecoder 来简化处理. 本篇将重点讨论这 ...

  9. java多线程与线程池-copy

    1. 场景描述 以前多线程也常用,这次因需再页面上用到多线程,如下图,总结下,有需要的朋友可以参考下. 2. 解决方案 2.1 线程池概念 线程池官方定义不说了,通俗说下:池子的概念,事先(预定义)创 ...

  10. 阿里云-数据库-ClickHouse

    https://help.aliyun.com/product/144466.html 云数据库ClickHouse是开源列式数据库管理系统ClickHouse在阿里云上的托管服务,用户可以在阿里云上 ...