服务治理:几种开源限流算法库/应用软件介绍和使用

🚀 优质资源分享 🚀

学习路线指引(点击解锁)知识定位人群定位
🧡 Python实战微信订餐小程序 🧡进阶级本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。
💛Python量化交易实战💛入门级手把手带你打造一个易扩展、更安全、效率更高的量化交易系统

一、Go time/rate 限流器

1.1 简介

Go 在 x 标准库,即 golang.org/x/time/rate 里自带了一个限流器,这个限流器是基于令牌桶算法(token bucket)实现的。

在上一篇文章讲了几种限流算法,里面就有令牌桶算法,具体可以看上篇文章介绍。

1.2 rate/time 限流构造器

这个限流构造器就是生成 token,供后面使用。

Limiter struct 结构:

Copy// https://github.com/golang/time/blob/master/rate/rate.go#L55// The methods AllowN, ReserveN, and WaitN consume n tokens.
type Limiter struct {mu sync.Mutexlimit Limit   // 放入 token 的速率burst int     // 令牌桶限制最大值tokens float64 // 桶中令牌数// last is the last time the limiter's tokens field was updatedlast time.Time// lastEvent is the latest time of a rate-limited event (past or future)lastEvent time.Time
}

限流器构造方法:func NewLimiter(r Limit, b int) *Limiter:

  • r :产生 token 的速率。默认是每秒中可以向桶中生产多少 token。也可以设置这个值,用方法 Every 设置 token 速率时间粒度。
  • b :桶的容量,桶容纳 token 的最大数量。 b == 0,允许声明容量为 0 的值,这时拒绝所有请求;与 b== 0 情况相反,如果 r 为 inf 时,将允许所有请求,即使是 b == 0。
Copy// Inf is the infinite rate limit; it allows all events (even if burst is zero). 
const Inf = Limit(math.MaxFloat64)

It implements a “token bucket” of size b, initially full and refilled at rate r tokens per second.
构造器一开始会为桶注入 b 个 token,然后每秒补充 r 个 token。

  • 每秒生成 20 个 token,桶的容量为 5,代码为:
Copylimiter := NewLimiter(20, 5)
  • 200ms 生成 1 个 token

这时候不是秒为单位生成 token ,就可以使用 Every 方法设置生成 token 的速率:

Copylimit := Every(200 * time.Millisecond)
limiter := NewLimiter(limit, 5)

1秒 = 200ms * 5,也就是每秒生成 5 个 token。

生成了 token 之后,请求获取 token,然后使用 token。

1.3 time/rate 有3种限流用法

time/rate 源码里注释,消费 n 个 tokens 的方法

// The methods AllowN, ReserveN, and WaitN consume n tokens.

  • AllowN
  • ReserveN
  • WaitN

A. WaitN、Wait

WaitN / Wait 方法:

Copy// https://pkg.go.dev/golang.org/x/time/rate#Limiter.WaitN
// WaitN blocks until lim permits n events to happen.
// It returns an error if n exceeds the Limiter's burst size, the Context is
// canceled, or the expected wait time exceeds the Context's Deadline.
// The burst limit is ignored if the rate limit is Inf.
func (lim *Limiter) WaitN(ctx context.Context, n int) (err error)func (lim *Limiter) Wait(ctx context.Context) (err error)

WaitN : 当桶中的 token 数量小于 N 时,WaitN 方法将阻塞一段时间直到 token 满足条件或超时或取消(如果设置了context),超时或取消将返回error。如果 N 充足则直接返回。
Wait : 就是 WaitN 方法中参数 n 为 1 时,即:WaitN(ctx, 1)

方法里还有 Contex 参数,所以也可以设置 Deadline 或 Timeout,来决定 Wait 最长时间。比如下面代码片段:

Copy ctx, cancel := context.WithTimeout(context.Background(), time.Second * 5)defer cancel()err := limiter.WaitN(ctx, 2)

例子1:

Copypackage mainimport ("context""fmt""time""golang.org/x/time/rate"
)func main() {limit := rate.NewLimiter(3, 5) // 每秒产生 3 个token,桶容量 5ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)defer cancel() // 超时取消for i := 0; ; i++ { // 有多少令牌直接消耗掉fmt.Printf("%03d %s\n", i, time.Now().Format("2006-01-02 15:04:05.000"))err := limit.Wait(ctx)if err != nil { // 超时取消 err != nilfmt.Println("err: ", err.Error())return // 超时取消,退出 for}}
}

分析:这里指定令牌桶大小为 5,每秒生成 3 个令牌。for 循环消耗令牌,产生多少令牌都会消耗掉。
从开始一直到 5 秒超时,计算令牌数,一开始初始化 NewLimiter 的 5 个 + 每秒 3 个令牌 * 5秒 ,总计 20 个令牌。运行程序输出看看:

Copy$ go run .\waitdemo.go
000 2022-05-17 21:35:38.400
001 2022-05-17 21:35:38.425
002 2022-05-17 21:35:38.425
003 2022-05-17 21:35:38.425
004 2022-05-17 21:35:38.425
005 2022-05-17 21:35:38.425
006 2022-05-17 21:35:38.773
007 2022-05-17 21:35:39.096
008 2022-05-17 21:35:39.436
009 2022-05-17 21:35:39.764
010 2022-05-17 21:35:40.106
011 2022-05-17 21:35:40.434
012 2022-05-17 21:35:40.762
013 2022-05-17 21:35:41.104
014 2022-05-17 21:35:41.430
015 2022-05-17 21:35:41.759
016 2022-05-17 21:35:42.104
017 2022-05-17 21:35:42.429
018 2022-05-17 21:35:42.773
019 2022-05-17 21:35:43.101
err:  rate: Wait(n=1) would exceed context deadline

B: AllowN、Allow

AllowN / Allow 方法

Copy// https://pkg.go.dev/golang.org/x/time/rate#Limiter.AllowN
// AllowN reports whether n events may happen at time now.
// Use this method if you intend to drop / skip events that exceed the rate limit.
// Otherwise use Reserve or Wait.
func (lim *Limiter) AllowN(now time.Time, n int) bool// Allow is shorthand for AllowN(time.Now(), 1).
func (lim *Limiter) Allow() bool

AllowN :截止到某一时刻,桶中的 token 数量至少为 N 个,满足就返回 true,同时从桶中消费 n 个 token;反之返回 false,不消费 token。这个实际就是丢弃某些请求。
Allow :就是 AllowN 方法中参数 now 为现在时间,n 为 1,即 AllowN(time.Now(), 1)

例子:

Copypackage mainimport ("fmt""net/http""time""golang.org/x/time/rate"
)func main() {r := rate.Every(1 * time.Millisecond)limit := rate.NewLimiter(r, 10)http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {if limit.Allow() {fmt.Printf("success,当前时间:%s\n", time.Now().Format("2006-01-02 15:04:05"))} else {fmt.Printf("success,但是被限流了。。。\n")}})fmt.Println("http start ... ")_ = http.ListenAndServe(":8080", nil)}

然后你可以找一个 http 测试工具模拟用户压测下,比如 https://github.com/rakyll/hey 这个工具。测试命令:

Copyhey -n 100 http://localhost:8080/

就可以看到输出的内容

… …
success,当前时间:2022-05-17 21:41:44
success,当前时间:2022-05-17 21:41:44
success,当前时间:2022-05-17 21:41:44
success,但是被限流了。。。
success,但是被限流了。。。
… …

例子2:

Copypackage mainimport ("fmt""time""golang.org/x/time/rate"
)func main() {limit := rate.NewLimiter(1, 3)for {if limit.AllowN(time.Now(), 2) {fmt.Println(time.Now().Format("2006-01-02 15:04:05"))} else {time.Sleep(time.Second * 3)}}
}

C:ReserveN、Reserve

ReserveN / Reserve 方法

Copy// https://pkg.go.dev/golang.org/x/time/rate#Limiter.ReserveN
// ReserveN returns a Reservation that indicates how long the caller must wait before n events happen. The Limiter takes this Reservation into account when allowing future events. The returned Reservation’s OK() method returns false if n exceeds the Limiter's burst size.
func (lim *Limiter) ReserveN(now time.Time, n int) *Reservationfunc (lim *Limiter) Reserve() *Reservationfunc (r *Reservation) DelayFrom(now time.Time) time.Duration
func (r *Reservation) Delay() time.Duration
func (r *Reservation) OK() bool

其实上面的 WaitN 和 AllowN 都是基于 ReserveN 方法。具体可以去看看这 3 个方法的源码。

ReserveN :此方法返回 *Reservation 对象。你可以调用该对象的 Dealy 方法,获取延迟等待的时间。如果为 0,则不用等待。必须等到等待时间结束后才能进行下面的工作。
或者,如果不想等待,可以调用 Cancel 方法,该方法会将 Token 归还。
Reserve :就是 ReserveN 方法中参数 now 为现在时间,n 为 1,即 AllowN(time.Now(), 1)

usage example:

Copy// https://pkg.go.dev/golang.org/x/time/rate#Limiter.ReserveNr := lim.ReserveN(time.Now(), 1)
if !r.OK() {// Not allowed to act! Did you remember to set lim.burst to be > 0 ?return
}
time.Sleep(r.Delay())
Act() // 执行相关逻辑

1.4 动态设置桶token容量和速率

SetBurstAt / SetBurst:

Copyfunc (lim *Limiter) SetBurstAt(now time.Time, newBurst int)
func (lim *Limiter) SetBurst(newBurst int)

SetBurstAt :设置到某时刻桶中 token 的容量
SetBurst:SetBurstAt(time.Now())

SetLimitAt / SetLimit

Copyfunc (lim *Limiter) SetLimitAt(now time.Time, newLimit Limit)
func (lim *Limiter) SetLimit(newLimit Limit)

SetLimitAt :设置某刻 token 的速率
SetLimit :设置 token 的速率

二、uber 的 rate limiter

2.1 简介

uber 的这个限流算法是漏桶算法(leaky bucket) - github.com/uber-go/ratelimit。

与令牌桶算法的区别:

  1. 漏桶算法流出的速率可以控制,流进桶中请求不能控制
  2. 令牌桶算法对于流入和流出的速度都是可以控制的,因为令牌可以自己生成。所以它还可以应对突发流量。突发流量生成 token 就快些。
  3. 令牌桶算法只要桶中有 token 就可以一直消费,漏桶是按照预定的间隔顺序进行消费的。

2.2 使用

官方的例子:

Copylimit := ratelimit.New(100) // 每秒钟允许100个请求prev := time.Now()for i := 0; i < 10; i++ {now := limit.Take()fmt.Println(i, now.Sub(prev))prev = now
}

限流器每秒可以通过 100 个请求,平均每个间隔 10ms。

2.3 uber 对漏桶算法的改进

在传统的漏桶算法,每个请求间隔是固定的,然而在实际应用中,流量不是这么平均的,时而小时而大,对于这种情况,uber 对 leaky bucket 做了一点改进,引入 maxSlack 最大松弛量的概念。

举例子:比如 3 个请求,请求 1 完成,15ms后,请求 2 才到来,可以对 2 立即处理。请求 2 完成后,5ms后,请求 3 到来,这个请求距离上次请求不足 10ms,因此要等 5ms。
但是,对于这种情况,实际三个请求一共耗时 25ms 才完成,并不是预期的 20ms。

uber 的改进是:可以把之情请求间隔比较长的时间,匀给后面的请求使用,只要保证每秒请求数即可。

uber ratelimit 改进代码实现:

Copyt.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
}

把每个请求多余出来的等待时间累加起来,以给后面的抵消使用。

其他参数用法:

  • WithoutSlack:

ratelimit 中引入最大松弛量,默认的最大松弛量为 10 个请求的间隔时间。
但是我不想用这个最大松弛量呢,就要限制请求的固定间隔时间,用 WithoutSlack 这个参数限制:

Copylimit := ratelimit.New(100, ratelimit.WithoutSlack)
  • WithClock(clock Clock):

ratelimit 中时间相关计算是用 go 的标准时间库 time,如果想要更高进度或特殊需求计算,可以用 WithClock 参数替换,实现 Clock 的 interface 就可以了

Copytype Clock interface {Now() time.TimeSleep(time.Duration)
}clock &= MyClock{}
limiter := ratelimit.New(100, ratelimit.WithClock(clock))

更多 ratelimit

三、其他限流器,算法库包和软件

  1. 滴滴的 tollbooth,http 限流中间件,有很多特性:
    • 1.基于IP,路径,方法,header,授权用户等限流
    • 2.通过使用 LimitByKeys() 组合你自己的中间件
    • 3.对于head项和基本auth能够设置TTL-过期时间
    • 4.拒绝后,可以使用以下 HTTP 头响应,比如 X-Rate-Limit-Limit The maximum request limit
    • 5.当限流达到上限,可以自定义消息和方法,返回信息
    • 6.它是基于 golang.org/x/time/rate 开发
  2. java 的 guava 限流
    • ratelimiter
  3. 基于信号量限流
    • https://github.com/golang/net/blob/master/netutil/listen.go
  4. sentinel-go 服务治理软件以及sentinel
    • https://github.com/alibaba/sentinel-golang
    • github.com/alibaba/Sentinel ,java编写的流控组件,服务治理
  5. 还有各种基于 nginx 的限流器,限流软件-服务网关,api gateway等

四、参考

  • https://github.com/golang/time
  • pkg.go.dev/golang.org/x/time/rate
  • https://zhuanlan.zhihu.com/p/100594314
  • https://segmentfault.com/a/1190000023033365
  • https://www.cyhone.com/articles/usage-of-golang-rate/
  • https://www.cyhone.com/articles/analysis-of-uber-go-ratelimit
  • uber ratelimit vs go time/rate demo
  • uber-go ratelimit

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/401000.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

iOS应用开发视频教程笔记(二)My First iOS App

这课主要是以一个计算器一个用为例子&#xff0c;教你怎么使用XCode&#xff0c;如何使用MVC设计模式创建应用。 (1)新建一个single view application模版的应用 打开xcode并点击“创建一个新xcode项目”&#xff0c;进入项目创建界面&#xff0c;这个界面让我们为应用选择一个…

xmemcached spring 配置文件

memcached spring 配置文件 <bean class"java.net.InetSocketAddress" name"server1"><constructor-arg><value>${memcached.server1.host}</value></constructor-arg><constructor-arg><value>${memcached.ser…

牛顿迭代法(Newton#39;s Method)

牛顿迭代法&#xff08;简称牛顿法&#xff09;由英国著名的数学家牛顿爵士最早提出。可是&#xff0c;这一方法在牛顿生前并未公开发表&#xff08;讨厌的数学家们还是鼓捣出来了&#xff09;牛顿法的作用是使用迭代的方法来求解函数方程的根。简单地说&#xff0c;牛顿法就是…

【深入理解计算机系统CSAPP】第六章 存储器层次结构

&#x1f680; 优质资源分享 &#x1f680; 学习路线指引&#xff08;点击解锁&#xff09;知识定位人群定位&#x1f9e1; Python实战微信订餐小程序 &#x1f9e1;进阶级本课程是python flask微信小程序的完美结合&#xff0c;从项目搭建到腾讯云部署上线&#xff0c;打造一…

【转】无刷新验证用户名可用性

在用户注册时&#xff0c;我们经常需要检查用户名是否存在&#xff0c;本文就是实现无刷新验证用户名 打开开发环境VS 2005,新建项目(或打开现有项目),新建一个Web窗体,命名为 Default.aspx 代码如下&#xff1a; View Code <% Page Language"C#" AutoEventWireu…

视图的创建

-- 创建数据库 create table emp( sid int(8) primary key, sname varchar(10), sex varchar(2), chu varchar(50), classno varchar(50) ); -- 表中的数据 insert into emp (sname,sex,chu,classno) values (张石瑞,男,1996-01-02,2600), (李佛,女,1998-05-15,3000), (王法无,…

压缩 js/css 的工具

最近检测服务器&#xff0c;发现js/css文件都没有压缩过&#xff0c;动手解决此问题先。 本次压缩采用 yui compress (2.4.8) 压缩脚本&#xff1a; #!/bin/sh echo "###########################" echo "---------------------------" echo "begin t…

Python数据分析--Numpy常用函数介绍(2)

&#x1f680; 优质资源分享 &#x1f680; 学习路线指引&#xff08;点击解锁&#xff09;知识定位人群定位&#x1f9e1; Python实战微信订餐小程序 &#x1f9e1;进阶级本课程是python flask微信小程序的完美结合&#xff0c;从项目搭建到腾讯云部署上线&#xff0c;打造一…

bzoj3224: Tyvj 1728 普通平衡树(打个splay暖暖手)

&#xff08;其实今天好热啊&#xff1f; 题目大意&#xff1a;插入&#xff0c;删除&#xff0c;k小&#xff0c;前驱后继&#xff0c;数的排名。 splay和treap裸题...过几天补个treap的 splay: #include<iostream> #include<cstdlib> #include<cstring> #i…

手机相机自动识别语音提示

技术背景&#xff1a; 时下流行的手机拍照功能越来越多&#xff0c;在众多的手机拍照过程中&#xff0c;我们只在于手机拍照的效果和风景是否美好&#xff0c;甚至拿着手机自我狂拍&#xff0c;留下美好的记忆和回忆。 有时候根据手机相机的已有技术功能随便一设置就能拍到理想…

c# Invoke和BeginInvoke 区别

转自http://www.cnblogs.com/c2303191/articles/826571.html Control的Invoke和BeginInvoke是相对于支线线程&#xff08;因为一般在支线线程中调用&#xff0c;用来更新主线程ui&#xff09;Invoke立即插入主线程中执行&#xff0c;而BeginInvoke 要等主线程结束才执行 近日&a…

04 Springboot 格式化LocalDateTime

&#x1f680; 优质资源分享 &#x1f680; 学习路线指引&#xff08;点击解锁&#xff09;知识定位人群定位&#x1f9e1; Python实战微信订餐小程序 &#x1f9e1;进阶级本课程是python flask微信小程序的完美结合&#xff0c;从项目搭建到腾讯云部署上线&#xff0c;打造一…

DNN使用非80端口和总是跳转到http://localhost问题的解决

2019独角兽企业重金招聘Python工程师标准>>> 我试图在一台服务器上安装一个DNN网站时&#xff0c;遇到了一些问题。问题一&#xff1a;遇到的第一个问题就是网站总是自动导向到localhost。不管我怎么试&#xff0c;只要我输入http://domain.com/dnn&#xff0c;总是…

解决解决鼠标右键被锁定

现象&#xff1a;我的电脑不知道为什么鼠标右键被锁定了&#xff0c;用不了。请问如何解决&#xff1f;可能是有人在你的注册表中做了手脚&#xff0c;锁定了右键菜单。打开注册表编辑器&#xff0c;选择以下子键&#xff1a;“HKEY_CURRENT_USER\Software\Microsoft\Windows\C…

Jmter操作数据库

1、导入jdbc的jar包&#xff0c;因为jmeter本身不能直接连接mysql&#xff0c;所以需要导入第三方的jar包&#xff0c;来连接mysql&#xff0c;如下操作&#xff1a;2、创建数据库连接如下&#xff1a; 3、配置mysql的url、端口号、账号、密码注意上面的Database URL&#xff1…

通过telnet连接查看memcache服务器

memcache作为一款优秀的进程外缓存&#xff0c;常常被运用于高并发系统架构中。这里主要谈谈怎么通过telnet工具&#xff0c;查看memcache运行状况并对其key进行管理维护。假设memcache安装目录&#xff1a;/usr/local/memcached 1、启动memcache [plain] view plaincopy [root…

Ubuntu的一些软件源

&#x1f680; 优质资源分享 &#x1f680; 学习路线指引&#xff08;点击解锁&#xff09;知识定位人群定位&#x1f9e1; Python实战微信订餐小程序 &#x1f9e1;进阶级本课程是python flask微信小程序的完美结合&#xff0c;从项目搭建到腾讯云部署上线&#xff0c;打造一…

ComponentOne FlexGrid for WinForms 中文版快速入门(9)—过滤

C1FlexGrid过滤 表格中的数据过滤通常有两种形式&#xff1a; 基于表头&#xff1a;过滤器的图标出现在有一个过滤器适用于它的每一列。用户可以通过点击过滤器的图标来查看和编辑过滤器。这是Windows 7或Vista或C1FlexGrid控件使用的机制。这种类型的过滤器的主要优点是&…

Pycharm搜索导航之文件名、符号名搜索

1、准备一个工程 向你的工程中添加一个Python文件&#xff0c;并输入一些源码&#xff0c;例如&#xff1a; 2、转到对应文件、类、符号 Pycharm提供的一个很强力的功能就是能够根据名称跳转到任何文件、类、符号所在定义位置。 3、跳转到文件 按下CtrlShiftN快捷键&#xff0c…

asp.net6 blazor 文件上传

&#x1f680; 优质资源分享 &#x1f680; 学习路线指引&#xff08;点击解锁&#xff09;知识定位人群定位&#x1f9e1; Python实战微信订餐小程序 &#x1f9e1;进阶级本课程是python flask微信小程序的完美结合&#xff0c;从项目搭建到腾讯云部署上线&#xff0c;打造一…