【从零单排Golang】第十四话:使用rate和ratelimit实现限流限速

news/2025/2/23 5:46:31/

在研发中,我们经常会面对到处理并发逻辑的场景,尤其是有时候在与第三方平台对接的场景下,会遇到请求限流限QPS的要求。对于限流或者限速,我们通常会用两种算法来满足需要:

  • 令牌桶算法:在特定容量的桶里面装令牌,当令牌数量小于桶的容量时,会持续以我们预期的限流速率生产令牌;不管桶里面是不是空的,业务都得等到拿到令牌,才能继续执行业务逻辑
  • 漏桶算法:业务先统一进入桶里,桶满了之后,会以我们预期的限流速率,一个个把在等待的业务漏出去,然后各个业务才开始执行业务逻辑

这两种算法,虽然实际QPS数值可能都会有波动,但都能把速率限制在一个合理的水位。在Golang里面,这两种算法都有现成的实现可以直接用。咱们今天,就来看看这块的例子。

首先来看看令牌桶算法,在Golang自带的rate库中,就有了一份令牌桶算法的实现。我们上一下代码的例子,来看一下rate库的基本用法:

import "golang.org/x/time/rate" // 需要import的rate库,其它import暂时忽略// 生成0->X的数据集
func generateData(num int) []any {var data []anyfor i := 0; i < num; i++ {data = append(data, i)}return data
}// 处理数据,数字*10
func process(obj any) (any, error) {integer, ok := obj.(int)if !ok {return nil, errors.New("invalid integer")}time.Sleep(1)nextInteger := integer * 10if integer%99 == 0 {return nextInteger, errors.New("not a happy number")}return nextInteger, nil
}func TestRate(t *testing.T) {limit := rate.Limit(50) // QPS:50burst := 25 // 桶容量25limiter := rate.NewLimiter(limit, burst)size := 500 // 数据量500data := generateData(size)var wg sync.WaitGroupstartTime := time.Now()for i, item := range data {wg.Add(1)go func(idx int, obj any) {defer wg.Done()// 拿到令牌if err := limiter.Wait(context.Background()); err != nil {t.Logf("[%d] [EXCEPTION] wait err: %v", idx, err)}// 执行业务逻辑processed, err := process(obj)if err != nil {t.Logf("[%d] [ERROR] processed: %v, err: %v", idx, processed, err)} else {t.Logf("[%d] [OK] processed: %v", idx, processed)}}(i, item)}wg.Wait()endTime := time.Now()t.Logf("start: %v, end: %v, seconds: %v", startTime, endTime, endTime.Sub(startTime).Seconds())
}

通过limiter := rate.NewLimiter(limit, burst)的形式,我们可以初始化一个令牌生成速率为limit,容量为burst的令牌桶。在业务里,则通过limiter.Wait(ctx)的方式拿到一个令牌,执行逻辑。结合先前讲过的WaitGroup,我们就能够实现开多个goroutine异步执行任务,并配合limiter来做业务逻辑的限速。

这里需要注意,从长线来看,limit的大小是能够决定一个基础的限速速率,但从短线角度来看,burst这个桶具备了缓冲作用,在冷启动时,由于burst的存在,初始的QPS会比实际预估的较大。因此,业务通过令牌桶方式限速时,如果需要限制冷启动时的瞬时速率,需要留意把burst的值设置小一些。

然后我们再来看通过漏桶算法来限速的例子,这回需要用到一个开源库uber-go/ratelimit来实现。例子也非常简单,代码如下:

func TestRateLimit(t *testing.T) {limiter := ratelimit.New(50) // 漏桶速率50size := 500data := generateData(size)var wg sync.WaitGroupstartTime := time.Now()for i, item := range data {wg.Add(1)go func(idx int, obj any) {defer wg.Done()limiter.Take() // 入桶待漏processed, err := process(obj)if err != nil {t.Logf("[%d] [ERROR] processed: %v, err: %v", idx, processed, err)} else {t.Logf("[%d] [OK] processed: %v", idx, processed)}}(i, item)}wg.Wait()endTime := time.Now()t.Logf("start: %v, end: %v, seconds: %v", startTime, endTime, endTime.Sub(startTime).Seconds())
}

ratelimit库速率的单位可以类比为QPS,通过ratelimit.New,可以初始化一个指定QPS限制的限流器。执行业务逻辑前,需要通过limiter.Take()逻辑等待漏出来后,才能执行后续的逻辑。这样便实现了限流的效果。


http://www.ppmy.cn/news/1065193.html

相关文章

【附安装】R语言4.3.0安装教程

软件下载 软件&#xff1a;R语言版本&#xff1a;4.3.0语言&#xff1a;简体中文大小&#xff1a;77.74M安装环境&#xff1a;Win7及以上版本&#xff0c;64位操作系统硬件要求&#xff1a;CPU2.0GHz 内存4G(或更高&#xff09;下载通道①百度网盘丨64位下载链接&#xff1a;h…

【Go 基础篇】Go 语言字符串函数详解:处理字符串进阶

大家好&#xff01;继续我们关于Go语言中字符串函数的探索。字符串是编程中常用的数据类型&#xff0c;而Go语言为我们提供了一系列实用的字符串函数&#xff0c;方便我们进行各种操作&#xff0c;如查找、截取、替换等。在上一篇博客的基础上&#xff0c;我们将继续介绍更多字…

编程题练习@8-26

题目一&#xff1a; 题目描述 你的团队最近更新了语音识别的算法&#xff0c;瑰需要对更新之后的算法模型进行识别率指标统计。 语音识别率指标通常为WER(Word Error Rate)即计算语音识别文本中出现错误的字总数占标准文本中总字数的比例。为了使识别出来的文本序列和标准的文本…

【LeetCode-面试经典150题-day15】

目录 104.二叉树的最大深度 100.相同的树 226.翻转二叉树 101.对称二叉树 105.从前序与中序遍历序列构造二叉树 106.从中序与后序遍历序列构造二叉树 117.填充每个节点的下一个右侧节点指针Ⅱ 104.二叉树的最大深度 题意&#xff1a; 给定一个二叉树 root &#xff0c;返回其…

R语言画样本不均衡组的箱线图

# 导入 ggplot2 包 library(ggplot2)# 示例数据框&#xff0c;包含数值数据和分组信息 data <- data.frame(Group c(rep("Group A",10), rep("Group B",15),rep("Group C",20)),Value c(rnorm(10, mean 10, sd 2),rnorm(15, mean 15, sd…

Python语言实现React框架

迷途小书童的 Note 读完需要 6分钟 速读仅需 2 分钟 1 reactpy 介绍 reactpy 是一个用 Python 语言实现的 ReactJS 框架。它可以让我们使用 Python 的方式来编写 React 的组件&#xff0c;构建用户界面。 reactpy 的目标是想要将 React 的优秀特性带入 Python 领域&#xff0c;…

php curl apache 超时 500错误

web请求超过40s 就返回500错误 php的超时时间 set_time_limit无效 curl CURLOPT_TIMEOUT 设置请求时间 无效 设置apache Timeout 链接超时 无效 最后添加 Fcgid才可以 apache 配置文件 httpd.conf <IfModule mod_fcgid.c>FcgidProcessLifeTime 10000FcgidIOTimeout 1000…

openGauss学习笔记-51 openGauss 高级特性-列存储

文章目录 openGauss学习笔记-51 openGauss 高级特性-列存储51.1 语法格式51.2 参数说明51.3 示例 openGauss学习笔记-51 openGauss 高级特性-列存储 openGauss支持行列混合存储。行存储是指将表按行存储到硬盘分区上&#xff0c;列存储是指将表按列存储到硬盘分区上。 行、列…