上次有同学分享了 单机限流器 time/rate 库,讲了 Golang 标准库中基于令牌桶实现限流组件的 time/rate 使用,同时也讲了一些限流算法原理。

这里分享一个 uber 开源的一套基于漏桶实现的用于服务限流的 golang 库 ratelimit。

漏洞算法的理解起来,相较于令牌桶,没有那么直观。因为令牌桶是限制 “令牌” 的速率,拿到令牌的请求才能被放行,而漏桶是限制单位时间内允许通过的请求数目,调用方只能严格按照预定的间隔顺序进行消费调用。

1

ratelimit 的基本使用

官方示例:

import ("fmt""time""go.uber.org/ratelimit"
)func main() {rl := ratelimit.New(100) // per secondprev := time.Now()for i := 0; i < 10; i++ {now := rl.Take()fmt.Println(i, now.Sub(prev))prev = now}
}

在这个例子中,我们给定限流器每秒可以通过 100 个请求,也就是平均每个请求间隔 10ms。因此,最终会每 10ms 打印一行数据。输出结果如下:

// Output:
// 0 0
// 1 10ms
// 2 10ms
// 3 10ms
// 4 10ms
// 5 10ms
// 6 10ms
// 7 10ms
// 8 10ms
// 9 10ms

2

高级用法

最大松弛量

传统的漏桶,每个请求的间隔是固定、匀速的,两次请求 req1 和 req2 之间的延迟至少应该 >=perRequest,然而,在实际上的应用中,流量经常是突发性的。对于突发这种情况,uber-go 对 Leaky Bucket 做了一些改良,引入了最大松弛量(maxSlack)的概念, 表示允许抵消的最长时间

漏桶算法的限速逻辑:

sleepFor = t.perRequest - now.Sub(t.last)
if sleepFor > 0 {t.clock.Sleep(sleepFor)t.last = now.Add(sleepFor)
} else {t.last = now
}

上面计算 sleepFor 的代码,不使用最大松弛量的 sleep 逻辑,严格要求两个请求之间必须间隔 t.perRequest 的时间,那么计算 sleepFor 的代码就是:

t.sleepFor = t.perRequest - now.Sub(t.last)

这样就会有以下问题:

我们假设现在有三个请求,req1req2req3,限速区间为 10msreq1 最先到来,当 req1 完成之后 15msreq2 才到来,依据限速策略可以对 req2 立即处理,当 req2 完成后,5ms 后, req3 到来,这个时候距离上次请求还不足 10ms,因此还需要等待 5ms 才能继续执行, 但是,对于这种情况,实际上这三个请求一共消耗了 25ms 才完成,并不是预期的 20ms

上面这个 case 模拟了一种请求量突发的状况。但这里我们可以把之前间隔比较长的请求的时间(累加超出 perRequest 的时延),匀给后面的请求判断限流时使用。对于上面的 case,因为 req2 相当于多等了 5ms,我们可以把这 5ms 移给 req3 使用。加上 req3 本身就是 5ms 之后过来的,一共刚好 10ms,所以 req3 无需等待,直接可以处理。此时三个请求也恰好一共是 20ms。如下:

t.sleepFor += t.perRequest - now.Sub(t.last)
if t.sleepFor > 0 {t.clock.Sleep(t.sleepFor)t.last = now.Add(t.sleepFor)t.sleepFor = 0
} else {t.last = now
}

t.sleepFor > 0,代表此前的请求多余出来的时间,无法完全抵消此次的所需量,因此需要 sleep 相应时间, 同时将 t.sleepFor 置为 0。

t.sleepFor < 0,说明此次请求间隔大于预期间隔,将多出来的时间累加到 t.sleepFor 即可。

但是,对于某种情况,请求 1 完成后,请求 2 过了很久到达 ,那么此时对于请求 2 的请求间隔 now.Sub(t.last),会非常大。以至于即使后面大量请求瞬时到达,也无法抵消完这个时间。那这样就失去了限流的意义。

为了防止这种情况,ratelimit 就引入了最大松弛量 (maxSlack) 的概念, 该值为负值,表示允许抵消的最长时间,防止以上情况的出现。

// 当计算出来的 sleepFor 超过一个 maxSlack 时,那么就只 sleep 一个 maxSlack 时间,用于两次请求时间间隔过大的场景
if t.sleepFor < t.maxSlack {t.sleepFor = t.maxSlack
}

ratelimit 中 maxSlack 的值为 -10 * time.Second / time.Duration(rate), 是十个请求的间隔大小。我们也可以理解为 ratelimit 允许的最大瞬时请求为 10。

用法

ratelimit 的 New 函数,除了可以配置每秒请求数 (QPS), 还提供了一套可选配置项 Option。

func New(rate int, opts ...Option) Limiter

WithoutSlack

上面说到 ratelimit 中引入了最大松弛量的概念,而且默认的最大松弛量为 10 个请求的间隔时间。

但是对于需要严格的限制请求的固定间隔的时,我们可以利用 WithoutSlack 来取消松弛量的影响。

limiter := ratelimit.New(100, ratelimit.WithoutSlack)

另外,ratelimit 还有其他选项,比如WithClock(clock Clock) 可以用来替换默认时钟,来实现精度更高或者特殊需求的计时场景。

3

Reference

uber-go 漏桶限流器使用与原理分析 | 编程沉思录 (cyhone.com)

开源限流组件分析(一):Uber 的 Leaky Bucket - 熊喵君的博客 | PANDAYCHEN

限流算法之一 | hzSomthing (hedzr.com)

uber-go/ratelimit: A Golang blocking leaky-bucket rate limit implementation (github.com)

《酷Go推荐》招募:

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

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

大概规则和每日新闻类似,如果报名人多的话每个人一个月轮到一次,欢迎大家报名!戳「阅读原文」,即可报名

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

「GoCN酷Go推荐」漏桶限流库 — uber-go/ratelimit相关推荐

  1. 「GoCN酷Go推荐」Golang的Ealstic链接库

    背景介绍 Elasticsearch是一个分布式.高扩展.高实时的搜索与数据分析引擎,用于海量文档的搜索.有些项目会将Elasticsearch当做存储海量数据的数据库使用,可见其查询性能之高效.作为 ...

  2. 「GoCN酷Go推荐」交互式命令行工具库survey

    ☆ 什么是 survey? ☆ survey 可以让你方便的在终端上构建交互式和可访问提示的应用,支持ANSI ☆ 安装 ☆ go get github.com/AlecAivazis/survey/ ...

  3. 「GoCN酷Go推荐」​QQ机器人 go-cqhttp

    什么是 go-cqhttp?✦ QQ机器人,可以做的事儿太多了,比如一个UP主需要群发多个QQ群,以便通知粉丝们开播:再比如可以检测群内或发给自己的消息,而通过代码直接回复做的简单回复.比如检测群内有 ...

  4. 「GoCN酷Go推荐」go语言位操作库 — bitset

    bitset库实现了bitsets数据结构,这是一种正整数和布尔值映射关系的结构,它比map[uint]bool更高效 什么是bitsets✦ bitsets基本思想是用一个bit位来标记某个元素对应 ...

  5. 「GoCN酷Go推荐」高性能内存缓存 ristretto

    背景 ristretto 是 dgraph 团队开源的一款高性能内存缓存库,旨在解决高并发场景下的缓存性能和吞吐瓶颈.dgraph 专攻的方向是高性能图数据库,ristretto 就是其图数据库和 K ...

  6. 「GoCN酷Go推荐」快速搭建私有云服务 go-btfs

    # 1. go-btfs 是什么?# go-btfs 是一个去中心化的文件存储平台,无论图片.文件.视频等等各类文件.每个人都可以在自己电脑上安装部署 BTFS 节点,然后大家的节点相互连接,构成一个 ...

  7. 「GoCN酷Go推荐」后现代时代远程办公网络问题的golang开源解决方案 —— PairMesh...

    PairMesh是什么? Pairmesh是一款先进的虚拟局域网(VPN)工具,用来搭建与访问安全,易用,高性能的点对点的软件定义虚拟局域网,为远程办公的网络连接问题提供了开箱即用的解决方案. 拥抱开 ...

  8. 「GoCN酷Go推荐」JSON 数据获取器 JID

    01 推荐理由   JSON 格式数据适用范围非常广泛,一个内容丰富的json数据可能很大,使用 JID 可以让你非常舒服的获取到想要到数据. 02 简介 JID 是一个过滤JSON格式数据 cli ...

  9. 「GoCN酷Go推荐」Golang轻量级桌面程序wails2教学

    01 推荐理由 不依赖cgo!不依赖cgo!不依赖cgo!真的不依赖cgo,且跨平台,原生渲染 无嵌入式浏览器,轻量级,生成的文件很小,而且只有一个可执行文件就可运行. 02 功能介绍 后端使用标准 ...

最新文章

  1. PHP上传图片到数据库和存储到本地文件夹的方法
  2. php 公交 查询系统,php定做单城市公交路线查询系统
  3. SAP UI5 初学者教程之二十六 - OData 服务配合 Mock 服务器的使用步骤详解试读版
  4. .Net Core2.*学习手册
  5. (C语言)链表的实现集合的相关操作
  6. vue-cli配置jquery 以及jquery第三方插件
  7. 一个可以下载Github指定子文件夹的Chrome插件
  8. 循环、选择要注意的复合语句
  9. Linux 数据重定向
  10. 多频电磁感应仪GEM-2介绍
  11. matlab怎么导入程序出错,Matlab导入数据时出错!十分困扰!
  12. 闲置路由器做无线打印服务器
  13. ESP32 文件存储 spiffs
  14. 多张JPG图片怎么转成一个PDF
  15. oracle lag使用情景,lag函数用法
  16. 关于 getWriter() has already been called for this response 的错误解决办法
  17. 学习笔记---原文网址在第一行
  18. 2022-2028中国聚合物增材制造设备市场现状研究分析与发展前景预测报告
  19. ANSYS CMD
  20. PCB——关于PCB设计倒角需要了解的一切(干货)

热门文章

  1. Java获取当天剩余时间的几种方法
  2. ImageJ按照Little-Endian格式存储raw文件
  3. js获取input file框(文件上传框)内容,上传后台
  4. 一位北大教授对韩国的评价,看完后笑抽
  5. 关于JS中addEventListener的使用
  6. 【单片机项目实训】基于nRF905的多点温度无线采集系统
  7. qtabwidget tab样式
  8. iOS动画一点也不神秘————你是喜欢看幻灯片?还是看高清电影?
  9. 微生物脂肪酸/长链烷烃单体C-13丰度测试服务重磅来袭
  10. 【笔记】电商订单数据分析实战