Golang官方限流器库实现限流示例详解
作者:jiaxwu 发布时间:2024-05-02 16:26:47
前言
在翻Golang官方库的过程中,发现一个有趣的库golang.org/x/time ,里面只有一个类rate,研究了一下发现它是一个限流器,实现了很多的功能,当然它的核心原理并不复杂,也就是令牌桶算法。
令牌桶算法的原理是:令牌桶会不断地把令牌添加到桶里,而请求会从桶中获取令牌,只有拥有令牌地请求才能被接受。因为桶中可以提前保留一些令牌,所以它允许一定地突发流量通过。
例子
下面是限流算法常见的写法,首先判断是否有令牌,如果有就通过,否则直接失败。
package main
import (
"fmt"
"time"
"golang.org/x/time/rate"
)
func main() {
// 每0.1秒生成一个令牌,也就是一秒10个令牌,最大保留令牌上限10
l := rate.NewLimiter(rate.Every(time.Second/10), 10)
for i := 0; i < 10; i++ {
go func(i int) {
for {
// 判断是否有令牌,如果有就输出
if l.Allow() {
fmt.Printf("allow %d\n", i)
}
// 每0.5秒请求一次
time.Sleep(time.Second / 2)
}
}(i)
}
time.Sleep(time.Second * 10)
}
上面的rate.Every(time.Second/10)会返回一个Limit类型,代表每秒生成多少个令牌。
这个库还提供了另外一种写法,等待直到有令牌为止(或超时):
func main() {
l := rate.NewLimiter(rate.Every(time.Second/10), 100)
for i := 0; i < 10; i++ {
go func(i int) {
for {
// 等待直到有令牌
if err := l.Wait(context.TODO()); err != nil {
} else {
fmt.Printf("allow %d\n", i)
}
time.Sleep(time.Second / 2)
}
}(i)
}
time.Sleep(time.Second * 10)
}
这样在某些场景下我们可以让请求等待一会,而不是直接失败。
还有一个更加特殊的请求令牌方式,也就是先预留令牌,到指定时间不再需要去获取令牌,直接执行操作即可:
func main() {
l := rate.NewLimiter(rate.Every(time.Second/10), 10)
for i := 0; i < 10; i++ {
go func(i int) {
for {
// 先预留令牌
if r := l.Reserve(); r.OK() {
// 休眠直到令牌生效
time.Sleep(r.Delay())
fmt.Printf("allow %d\n", i)
}
time.Sleep(time.Second / 2)
}
}(i)
}
time.Sleep(time.Second * 10)
}
当然,如果预留的令牌不想使用了,也可以使用r.Cancel()归还已预留的令牌。
上面的Allow()、Wait()、Reserve()都是一次消耗一个令牌,其实都有对应的AllowN()、WaitN()、ReserveN()方法,一次消耗N个令牌,这样就可以根据任务消耗的资源灵活的消耗令牌。
实现
不管我们从Allow()、Wait()还是Reserve()进去,最终都会进入到reserveN(now time.Time, n int, maxFutureReserve time.Duration) Reservation 方法:
首先在进入方法的时候,会先处理两种特殊情况:
// 如果无限生成令牌,则直接返回
if lim.limit == Inf {
return Reservation{
ok: true,
lim: lim,
tokens: n,
timeToAct: now,
}
// 如果不会生成令牌,则在初始令牌里面拿,直到拿完为止
} else if lim.limit == 0 {
var ok bool
if lim.burst >= n {
ok = true
lim.burst -= n
}
return Reservation{
ok: ok,
lim: lim,
tokens: lim.burst,
timeToAct: now,
}
}
然后重新计算当前有多少令牌,减去要消耗的令牌:
// 计算当前有多少令牌
now, last, tokens := lim.advance(now)
// 减去要消耗的N个令牌
tokens -= float64(n)
// 如果剩余令牌为负数,那么计算一下要等待多久才能拿到令牌
var waitDuration time.Duration
if tokens < 0 {
waitDuration = lim.limit.durationFromTokens(-tokens)
}
// 判断请求是否成功,maxFutureReserve代表最大可以等待的时间,也就是请求能否接收拿到令牌需要等待的时间
ok := n <= lim.burst && waitDuration <= maxFutureReserve
余下的代码就是更新限流器的状态。
小结
可以看到这个令牌桶限流器实现的功能非常的丰富,如果需要令牌桶限流器,可以优先考虑使用这个实现。
来源:https://juejin.cn/post/7130890097947312158
猜你喜欢
- 上次我们已经搞定了逻辑层的单元测试,这次我们来康康接口层的单元测试。接口层主要负责的就是请求的处理,最常见的就是 HTTP 请求的处理。但针
- 本文实例讲述了PHP面向对象程序设计类的定义与用法。分享给大家供大家参考,具体如下:<?phpclass Person {  
- 本文实例讲述了SQLSERVER简单创建DBLINK操作远程服务器数据库的方法。分享给大家供大家参考,具体如下:--配置SQLSERVER数
- 视图函数中加上认证功能,流程见下图import hashlibimport timedef get_random(name):
- 从ResNet到DenseNet上图中,左边是ResNet,右边是DenseNet,它们在跨层上的主要区别是:使用相加和使用连结。最后,将这
- MySql Server安装步骤如下所示:1安装MySql Server2 安装MySqlServer管理工具解压中文语言包,将文件复制到安
- 本文实例讲述了Python设计模式之代理模式。分享给大家供大家参考,具体如下:代理模式(Proxy Pattern):为其他对象提供一种代理
- 一般情况下,使用pip命令安装即可:[root@dthost27 ~]# pip install mysql-python但是在实际工作环境
- 主题的修改:File -> Settings ,弹出的窗口中: Appearance & Behavior -> App
- 第一步.开启mysql慢查询方式一:修改配置文件Windows:Windows 的配置文件为 my.ini,一般在 MySQL 的安装目录下
- 本文使用案例是基于 python2.7 实现以下内容均为个人使用 peewee 的经验和遇到的坑,不会涉及过多的基本操作。所以,没有使用过
- 题目:请求出任意区间[a,b]的所有素数,简单考虑实用性这道题看起来应该很easy是吧,但任意区间(这个问题有没get 到)Afanty的分
- DCL(Data Control Language):数据控制语言,用来定义数据库的访问权限和安全级别,及创建用户。一、管理用户1、创建用户
- 一、实验内容 对于下面这幅图像,编程实现染色体计数,并附简要处理流程说明。二、实验步骤1.中值滤波2.图像二值化3.膨胀图像4.腐
- 1.os.system函数wget 是一个下载软件的程序,如果已经下载好该软件,可以用py调用该软件。假如该软件目录在d:\tools\wg
- 是建立一个数据集 前面应该先定义此数据集 dim rs as adodb.recordset 然后就可以用 set rs=server.Cr
- 本文实例讲述了python类装饰器用法。分享给大家供大家参考。具体如下:#!coding=utf-8 registry = {} def r
- 此处收集的是一些夺人心魄的创意广告牌,巧妙的构思十分值得大家观瞻.......超人归来 superman returnsNespressos
- 如何快速的复制一张表首先创建一张表db1.t,并且插入1000行数据,同时创建一个相同结构的表db2.t假设,现在需要把db1.t里面的a&
- 先说下自己的环境,redis是部署在centos上的,爬虫运行在windows上,1. 安装redisyum install -y redi