当前位置: 首页 > news >正文

【Gin-Web】Bluebell社区项目梳理6:限流策略-漏桶与令牌桶

本文目录

  • 一、限流
  • 二、漏桶
  • 三、令牌桶算法
  • 四、Gin框架中实现令牌桶限流

一、限流

限流又称为流量控制,也就是流控,通常是指限制到达系统的并发请求数

限流虽然会影响部分用户的使用体验,但是能一定程度上保证系统的稳定性,不至于崩溃。

常见的各种厂商的公开API服务通常也会限制用户的请求次数,比如百度地图的API来限制请求数等。

二、漏桶

漏桶是一种比较常见的限流策略。

一句话来概括漏洞的核心就是:数据以任意速率进入漏桶,但是漏桶以固定速率(通常是预先设定的速率)将数据输出。

有点像削峰填谷,但是它缺点也很明显,就是不能很好处理有大量突发请求的场景。

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}// Output:// 0 0// 1 10ms// 2 10ms// 3 10ms// 4 10ms// 5 10ms// 6 10ms// 7 10ms// 8 10ms// 9 10ms
}

上面你的代码是uber团队开源的漏桶库ratelimit,这个库比较简单。

ratelimit.New(100) 创建了一个速率限制器(ratelimit.Limiter),限制频率为每秒 100 次。这里的 100 表示每秒最多可以获取 100 次令牌(tokens),即每秒最多可以执行 100 次操作。

prev 是一个 time.Time 类型的变量,用于记录上一次操作的时间。初始值为当前时间。

rl.Take()这是 ratelimit.Limiter 的一个方法,用于获取一个令牌。如果当前时间距离上一次获取令牌的时间小于 1/100 秒(因为每秒 100 次),Take() 方法会阻塞,直到下一个令牌可用。now 是获取令牌时的时间点(time.Time 类型)。

now.Sub(prev) 计算当前时间点 now 和上一次时间点 prev 之间的时间差(返回 time.Duration 类型)。这个时间差表示两次操作之间的时间间隔。

所以这段代码的目的是通过 ratelimit.New(100) 创建一个每秒最多执行 100 次操作的速率限制器,并在循环中模拟 10 次操作。每次操作之间的时间间隔会被打印出来,用于观察速率限制器是否按预期工作。

我们来看看这个限流器的实现源码。

限制器是一个接口类型,其要求实现一个Take()方法:

type Limiter interface {// Take方法应该阻塞已确保满足 RPSTake() time.Time
}

实现限制器接口的结构体定义如下,这里可以重点留意下maxSlack字段,它在后面的Take()方法中的处理。

type limiter struct {sync.Mutex                // 锁last       time.Time      // 上一次的时刻sleepFor   time.Duration  // 需要等待的时间perRequest time.Duration  // 每次的时间间隔maxSlack   time.Duration  // 最大的富余量clock      Clock          // 时钟
}

limiter结构体实现Limiter接口的Take()方法内容如下:

// Take 会阻塞确保两次请求之间的时间走完
// Take 调用平均数为 time.Second/rate.
func (t *limiter) Take() time.Time {t.Lock()defer t.Unlock()now := t.clock.Now()// 如果是第一次请求就直接放行if t.last.IsZero() {t.last = nowreturn t.last}// sleepFor 根据 perRequest 和上一次请求的时刻计算应该sleep的时间// 由于每次请求间隔的时间可能会超过perRequest, 所以这个数字可能为负数,并在多个请求之间累加t.sleepFor += t.perRequest - now.Sub(t.last)// 我们不应该让sleepFor负的太多,因为这意味着一个服务在短时间内慢了很多随后会得到更高的RPS。if t.sleepFor < t.maxSlack {t.sleepFor = t.maxSlack}// 如果 sleepFor 是正值那么就 sleepif t.sleepFor > 0 {t.clock.Sleep(t.sleepFor)t.last = now.Add(t.sleepFor)t.sleepFor = 0} else {t.last = now}return t.last
}

上面的代码根据记录每次请求的间隔时间和上一次请求的时刻来计算当次请求需要阻塞的时间——sleepFor,这里需要留意的是sleepFor的值可能为负,在经过间隔时间长的两次访问之后会导致随后大量的请求被放行,所以代码中针对这个场景有专门的优化处理。创建限制器的New()函数中会为maxSlack设置初始值,也可以通过WithoutSlack这个Option取消这个默认值。

func New(rate int, opts ...Option) Limiter {l := &limiter{perRequest: time.Second / time.Duration(rate),maxSlack:   -10 * time.Second / time.Duration(rate),}for _, opt := range opts {opt(l)}if l.clock == nil {l.clock = clock.New()}return l
}

三、令牌桶算法

令牌桶按固定的速率往桶里放入令牌,并且只要能从桶里取出令牌就能通过,令牌桶支持突发流量的快速处理。

如果放令牌的速率很快,那么令牌桶里边有很多令牌了,这个时候当有大量的客户端请求过来了,那么就可以直接取出令牌处理请求。

也就是令牌以固定的速率(例如每秒生成若干个令牌)被添加到桶中,桶有一个最大容量,当桶满时,多余的令牌会被丢弃。

次发送数据(如网络请求或数据包)时,需要从桶中取出一定数量的令牌。如果桶中有足够的令牌,则允许发送数据,并从桶中扣除相应的令牌;如果令牌不足,则请求被拒绝或排队等待。

由于桶中的令牌可以积累,因此在短时间内允许突发流量(bursty traffic),只要平均速率符合限制。

对于从桶里取不到令牌的场景,我们可以选择等待也可以直接拒绝并返回。这个需要根据业务场景来处理,如果业务场景不同,那么相对应的处理也就需要不一样。

可以参照github上的开源库:github.com/juju/ratelimit库。

创建令牌桶的方法如下:

// 创建指定填充速率和容量大小的令牌桶
func NewBucket(fillInterval time.Duration, capacity int64) *Bucket// 创建指定填充速率、容量大小和每次填充的令牌数的令牌桶
func NewBucketWithQuantum(fillInterval time.Duration, capacity, quantum int64) *Bucket// 创建填充速度为指定速率和容量大小的令牌桶
// NewBucketWithRate(0.1, 200) 表示每秒填充20个令牌
func NewBucketWithRate(rate float64, capacity int64) *Bucket

取出令牌的方法如下:

// 取token(非阻塞)
func (tb *Bucket) Take(count int64) time.Duration
func (tb *Bucket) TakeAvailable(count int64) int64// 最多等maxWait时间取token
func (tb *Bucket) TakeMaxDuration(count int64, maxWait time.Duration) (time.Duration, bool)// 取token(阻塞)
func (tb *Bucket) Wait(count int64)
func (tb *Bucket) WaitMaxDuration(count int64, maxWait time.Duration) bool

(注意这个令牌桶跟JWT算法中的Token并不是一个概念。)

虽说是令牌桶但没有必要真的去生成令牌放到桶里,我们只需要每次来取令牌的时候计算一下,当前是否有足够的令牌就可以了,具体的计算方式可以总结为下面的公式:

当前令牌数 = 上一次剩余的令牌数 + (本次取令牌的时刻-上一次取令牌的时刻)/放置令牌的时间间隔 * 每次放置的令牌数

四、Gin框架中实现令牌桶限流

这里我们可以将限流组件定义成中间件,因为中间件是在所有请求的必经之路上。按照需要限流的策略将中间件添加到不同的地方,如果要对全站进行限流就可以添加成全局中间件。如果某一组路由需要限流,就添加到对应的路由组就行。

func RateLimitMiddleware(fillInterval time.Duration, cap int64) func(c *gin.Context) {bucket := ratelimit.NewBucket(fillInterval, cap)return func(c *gin.Context) {// 如果取不到令牌就中断本次请求返回 rate limit...if bucket.TakeAvailable(1) == 0 {c.String(http.StatusOK, "rate limit...")c.Abort()return}// 取到令牌就放行c.Next()}
}

cap是填充速率,比如一秒钟填一个等。

然后我们在路由中生成即可,这样全网站就限速了。
在这里插入图片描述

相关文章:

【Gin-Web】Bluebell社区项目梳理6:限流策略-漏桶与令牌桶

本文目录 一、限流二、漏桶三、令牌桶算法四、Gin框架中实现令牌桶限流 一、限流 限流又称为流量控制&#xff0c;也就是流控&#xff0c;通常是指限制到达系统的并发请求数。 限流虽然会影响部分用户的使用体验&#xff0c;但是能一定程度上保证系统的稳定性&#xff0c;不至…...

51单片机-AT24CXX存储器工作原理

1、AT24CXX存储器工作原理 1.1、特点&#xff1a; 与400KHz&#xff0c;I2C总线兼容1.8到6.0伏工作电压范围低功耗CMOS技术写保护功能当WP为高电平时进入写保护状态页写缓冲器自定时擦写周期100万次编程/擦除周期可保存数据100年8脚DIP SOIC或TSSOP封装温度范围商业级和工业级…...

突破性能极限:DeepSeek开源FlashMLA解码内核技术解析

引言&#xff1a;大模型时代的推理加速革命 在生成式AI大行其道的今天&#xff0c;如何提升大语言模型的推理效率已成为行业焦点。DeepSeek团队最新开源的FlashMLA项目凭借其惊人的性能表现引发关注——在H800 GPU上实现580 TFLOPS计算性能&#xff0c;这正是大模型推理优化的…...

点击修改按钮图片显示有问题

问题可能出在表单数据的初始化上。在 ave-form.vue 中&#xff0c;我们需要处理一下从后端返回的图片数据&#xff0c;因为它们可能是 JSON 字符串格式。 vue:src/views/tools/fake-strategy/components/ave-form.vue// ... existing code ...Watch(value)watchValue(v: any) …...

[AI]从零开始的树莓派运行DeepSeek模型教程

一、前言 在前面的教程中&#xff0c;教了大家如何在windows中使用llama.cpp来运行DeepSeek模型。根据前面的教程中&#xff0c;我们也了解到了&#xff0c;我们只需要编译好llama.cpp就可以运行DeepSeek以及类似的LLM模型。那么本次教程就来教大家如何使用树莓派来运行大模型。…...

2024-2025 学年广东省职业院校技能大赛 “信息安全管理与评估”赛项 技能测试试卷(二)

2024-2025 学年广东省职业院校技能大赛 “信息安全管理与评估”赛项 技能测试试卷&#xff08;二&#xff09; 第一部分&#xff1a;网络平台搭建与设备安全防护任务书第二部分&#xff1a;网络安全事件响应、数字取证调查、应用程序安全任务书任务 1&#xff1a;应急响应&…...

Open WebUI本地部署教程

文章目录 1、系统环境配置2、源码下载2.1 github源码地址下载 3、环境启动3.1 后端环境3.2 前端环境 4、问题4.1 浏览器跨域问题4.2 all-MiniLM-L6-v2模型文件下载失败问题4.3 单独部署backend启动报错问题 1、系统环境配置 操作系统&#xff1a;windows/linux/macos Python版…...

Missing required prop: “maxlength“

背景&#xff1a; 封装一个使用功能相同使用频率较高的input公共组件作为子组件&#xff0c;大多数长度要求为200&#xff0c;且实时显示统计子数&#xff0c;部分input有输入提示。 代码实现如下&#xff1a; <template><el-input v-model"inputValue" t…...

dify本地部署

安装docker。 在官网安装docker。 如果遇到wsl报错&#xff0c;就使用 wsl --updata 进行更新。如果问题解决&#xff0c;进入docker应该是如下界面&#xff1a; 克隆 在自己创建的文件内使用 git clone gitgithub.com:langgenius/dify.git 或 git clone https://github.com…...

python学习一

学习网络安全为什么要学python? 1、在实际的渗透测试过程中,面对复杂多变的网络环境,当常用工 具不能满足实际需求的时候,往往需要对现有工具进行扩展,或者 编写符合我们要求的工具、自动化脚本,这个时候就需要具备一定 的编程能力。 2、python是一门编程语言经常用它…...

git branch

文章目录 1.简介2.格式3.选项4.示例参考文献 1.简介 git branch 用于管理分支&#xff0c;包括查看、创建、删除、重命名和关联。 git branch 是 Git 版本控制系统中用于管理分支的命令。分支是 Git 的核心功能之一&#xff0c;允许开发者在同一个代码库中并行开发不同的功能…...

算法-图-数据结构(邻接矩阵)-BFS广度优先遍历

邻接矩阵广度优先遍历&#xff08;BFS&#xff09;是一种用于遍历或搜索图的算法&#xff0c;以下是具体介绍&#xff1a; 1. 基本概念 图是一种非线性的数据结构&#xff0c;由顶点和边组成&#xff0c;可分为无向图、有向图、加权图、无权图等。邻接矩阵是表示图的一种数…...

数学建模之数学模型—2:非线性规划

文章目录 非线性规划基本概念与结论凸集与凸函数极值条件无约束条件的极值判断条件有约束条件的极值判断条件 无约束非线性规划一维搜索算法步骤示例特点代码模板 最速下降法算法详细步骤 代码实现示例最优步长的求解 黄金分割法斐波那契法牛顿法阻尼牛顿法模式搜索法Powell方法…...

unity学习51:所有UI的父物体:canvas画布

目录 1 下载资源 1.1 在window / Asset store下下载一套免费的UI资源 1.2 下载&#xff0c;导入import 1.3 导入后在 project / Asset下面可以看到 2 画布canvas&#xff0c;UI的父物体 2.1 创建canvas 2.1.1 画布的下面是 event system是UI相关的事件系统 2.2 canvas…...

ctfshow做题笔记—栈溢出—pwn57~pwn60

目录 前言 一、pwn57&#xff08;先了解一下简单的64位shellcode吧&#xff09; 二、pwn58 三、pwn59&#xff08;64位 无限制&#xff09; 四、pwn60&#xff08;入门难度shellcode&#xff09; 前言 往前写了几道题&#xff0c;与shellcode有关&#xff0c;关于shellc…...

数据结构 1-2 线性表的链式存储-链表

1 原理 顺序表的缺点&#xff1a; 插入和删除移动大量元素数组的大小不好控制占用一大段连续的存储空间&#xff0c;造成很多碎片 链表规避了上述顺序表缺点 逻辑上相邻的两个元素在物理位置上不相邻 头结点 L&#xff1a;头指针 头指针&#xff1a;链表中第一个结点的存储…...

ArcGIS Pro进行坡度与坡向分析

在地理信息系统中&#xff0c;坡度分析是一项至关重要的空间分析方法&#xff0c;旨在精确计算地表或地形的坡度&#xff0c;为地形特征识别、土地资源规划、环境保护、灾害预警等领域提供科学依据。本文将详细介绍如何利用ArcGIS Pro这一强大的地理信息系统软件&#xff0c;进…...

My first Android application

界面元素组成&#xff1a; 功能代码&#xff1a; /*实现功能&#xff1a;当输入内容后&#xff0c;欢迎文本发生相应改变&#xff0c;并清除掉文本域内容当未输入任何内容时&#xff0c;弹出提示文本以警告用户*/val greetingText findViewById<TextView>(R.id.printer)…...

ZLMediaKi集群设置

要在集群环境中部署 ZLMediaKit&#xff0c;您可以按照以下步骤进行操作。ZLMediaKit 是一个高性能的流媒体服务器&#xff0c;支持 RTMP、RTSP、HLS 等协议。以下是一个详细的集群部署方案&#xff1a; ### 1. 环境准备 - **服务器**&#xff1a;准备多台服务器&#xff0c;…...

Docker基础实践与应用举例

Docker 是一个轻量级容器化平台&#xff0c;通过将应用及其依赖打包到容器中&#xff0c;实现快速部署和环境一致性。以下是 Docker 的实践与应用场景举例&#xff0c;结合具体操作步骤&#xff1a; 一、基础实践 1. 快速启动一个容器 # 运行一个Nginx容器&#xff0c;映射宿…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…...

vscode(仍待补充)

写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh&#xff1f; debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)

可以使用Sqliteviz这个网站免费编写sql语句&#xff0c;它能够让用户直接在浏览器内练习SQL的语法&#xff0c;不需要安装任何软件。 链接如下&#xff1a; sqliteviz 注意&#xff1a; 在转写SQL语法时&#xff0c;关键字之间有一个特定的顺序&#xff0c;这个顺序会影响到…...

DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”

目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

网站指纹识别

网站指纹识别 网站的最基本组成&#xff1a;服务器&#xff08;操作系统&#xff09;、中间件&#xff08;web容器&#xff09;、脚本语言、数据厍 为什么要了解这些&#xff1f;举个例子&#xff1a;发现了一个文件读取漏洞&#xff0c;我们需要读/etc/passwd&#xff0c;如…...

回溯算法学习

一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...

rknn toolkit2搭建和推理

安装Miniconda Miniconda - Anaconda Miniconda 选择一个 新的 版本 &#xff0c;不用和RKNN的python版本保持一致 使用 ./xxx.sh进行安装 下面配置一下载源 # 清华大学源&#xff08;最常用&#xff09; conda config --add channels https://mirrors.tuna.tsinghua.edu.cn…...

DiscuzX3.5发帖json api

参考文章&#xff1a;PHP实现独立Discuz站外发帖(直连操作数据库)_discuz 发帖api-CSDN博客 简单改造了一下&#xff0c;适配我自己的需求 有一个站点存在多个采集站&#xff0c;我想通过主站拿标题&#xff0c;采集站拿内容 使用到的sql如下 CREATE TABLE pre_forum_post_…...

CppCon 2015 学习:REFLECTION TECHNIQUES IN C++

关于 Reflection&#xff08;反射&#xff09; 这个概念&#xff0c;总结一下&#xff1a; Reflection&#xff08;反射&#xff09;是什么&#xff1f; 反射是对类型的自我检查能力&#xff08;Introspection&#xff09; 可以查看类的成员变量、成员函数等信息。反射允许枚…...