在这里插入图片描述

本文目录

  • 1. 令牌桶算法
  • 2. 调用第三方库实现令牌桶
  • 3. 手撕令牌桶

前言:之前在Bluebell社区项目中,我们使用了开源的库来实现令牌桶限流,这次我们试着使用Go来手撕实现下令牌桶算法。

1. 令牌桶算法

为了防止网络拥塞,需要限制流出或者流入网络的流量,使流量以比较均匀的速度向外发送。令牌桶算法就实现了这个功能,可控制发送到网络上数据的数目,并允许突发数据的发送。

令牌桶算法是网络流量整形和速率限制中最常使用的一种算法。大小固定的令牌桶可自行以恒定的速率源源不断地产生令牌。如果令牌不被消耗,或者被消耗的速度小于产生的速度,令牌就会不断地增多,直到把桶填满。后面再产生的令牌就会从桶中溢出。最后桶中可以保存的最大令牌数永远不会超过桶的大小。

在这里插入图片描述

传送到令牌桶的数据包需要消耗令牌。不同大小的数据包,消耗的令牌数量不一样

令牌桶中的每一个令牌都代表一次请求或者一个字节。如果令牌桶中存在令牌,则允许发送流量;而如果令牌桶中不存在令牌,则不允许发送流量。因此,如果突发门限被合理地配置并且令牌桶中有足够的令牌,那么流量就可以以峰值速率发送。

2. 调用第三方库实现令牌桶

上次我们调用了封装好的令牌桶算法,代码如下。

回顾一下关键点,fillInterval time.Duration:表示令牌桶的填充间隔时间(例如,每秒填充一次)。cap int64:表示令牌桶的最大容量(即桶中最多可以存储的令牌数量)。

它返回一个函数,该函数的签名是 func(c *gin.Context),这符合 gin 中间件的标准形式。也就是接收一个 *gin.Context 的参数,用于处理每个 HTTP 请求。

这里的 func(c *gin.Context) { ... } 是一个匿名函数。它捕获 bucket 变量(闭包特性),因此可以在每次 HTTP 请求时使用同一个 bucket 实例。

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()}
}

我们在路由组中使用Use调用中间件,代码如下。

在这里插入图片描述

r.Use(Logger.GinLogger(), Logger.GinRecovery(stack: true), middlewares.RateLimitMiddleware(2*time.Second, cap: 1))

Logger.GinLogger():添加了一个日志记录中间件,用于记录 HTTP 请求的日志信息。

Logger.GinRecovery(stack: true):添加了一个错误恢复中间件,用于捕获和处理在请求处理过程中发生的任何 panic,并可选地记录堆栈跟踪。

middlewares.RateLimitMiddleware(2*time.Second, cap: 1):添加了速率限制中间件,配置为每两秒钟向令牌桶中添加一个令牌,桶的容量为 1。这意味着在任何给定的两秒时间内,最多只能处理一个请求。

3. 手撕令牌桶

直接上代码吧,逻辑比较简单。咱们来看看总体代码,再进行部分讲解。

package awesomeProjectimport ("sync""time"
)// 定义令牌桶结构
type tokenBucket struct {limitRate int           // 限制频率,即每分钟加入多少个令牌tokenChan chan struct{} // 令牌通道,可以理解为桶cap       int           // 令牌桶的容量muLock    *sync.Mutex   // 令牌桶锁,保证线程安全stop      bool          // 停止标记,结束令牌桶
}// NewTokenBucket 创建令牌桶
func NewTokenBucket(limitRate, cap int) *tokenBucket {if cap < 1 {panic("token bucket cap must be large 1")}return &tokenBucket{tokenChan: make(chan struct{}, cap),limitRate: limitRate,muLock:    new(sync.Mutex),cap:       cap,}
}// Start 开启令牌桶
func (b *tokenBucket) Start() {go b.produce()
}// 生产令牌
func (b *tokenBucket) produce() {for {b.muLock.Lock()if b.stop {close(b.tokenChan)b.muLock.Unlock()return}b.tokenChan <- struct{}d := time.Minute / time.Duration(b.limitRate)b.muLock.Unlock()time.Sleep(d)}
}// Consume 消费令牌
func (b *tokenBucket) Consume() {<-b.tokenChan
}// Stop 停止令牌桶
func (b *tokenBucket) Stop() {b.muLock.Lock()defer b.muLock.Unlock()b.stop = true
}

func NewTokenBucket(limitRate, cap int) *tokenBucket {if cap < 1 {panic("token bucket cap must be large 1")}return &tokenBucket{tokenChan: make(chan struct{}, cap),limitRate: limitRate,muLock:    new(sync.Mutex),cap:       cap,}
}

tokenChan chan struct{}tokenChan 是一个类型为 chan struct{} 的通道,用于存储令牌。

make(chan struct{}, cap) 创建了一个容量为 cap 的缓冲通道。这里 struct{} 表示通道中存储的是空结构体,仅用于占位,因为我们关心的是通道中元素的数量,而不是元素的值。这个通道模拟了令牌桶中令牌的数量,通道中的每个空结构体代表桶中的一个令牌。

func (b *tokenBucket) produce() {for {b.muLock.Lock()if b.stop {close(b.tokenChan)b.muLock.Unlock()return}b.tokenChan <- struct{}d := time.Minute / time.Duration(b.limitRate)b.muLock.Unlock()time.Sleep(d)}
}

time.Duration 是 Go 语言中用于表示时间间隔的类型,可以用于 time 包中的各种时间计算。
time.Minute 是一个常量,表示一分钟的时间间隔。b.limitRate 是一个整数,表示每分钟生成的令牌数量。


这里的通道chanstruct{}类型的,所以可以是别的。这里我们用struc{}空结构体,只是因为空结构体占用的内存空间非常小,适合用作通道中的占位符。

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

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

相关文章

C#开发的Base64编码及解码完整源码及注意事项

在软件开发时&#xff0c;经常用Base64编码和解码功能。本文介绍一个简单易用的Base64 编码和解码工具&#xff0c;顾名思义&#xff0c;就是简单快捷地进行 Base64 代码的解码或编码操作。您的数据可以轻松地编码为 Base64 编码&#xff0c;也可以解码为可读的格式。传输数据时…

【Linux第一弹】Linux基础指令(上)

目录 1.ls指令 1.1 ls使用实例 2.pwd指令 3.cd指令 3.1 cd使用实例 4.touch指令 4.1touch使用实例 5.mkdir指令 5.1mkdir使用实例 6.rmdir指令和rm指令 6.1 rmdir指令使用实例->: 6.2 rm指令使用实例 7.man指令 8.cp指令 8.1 cp 使用实例 9.mv指令 9.1mv使用…

RabbitMQ系列(七)基本概念之Channel

RabbitMQ 中的 Channel&#xff08;信道&#xff09; 是客户端与 RabbitMQ 服务器通信的虚拟会话通道&#xff0c;其核心作用在于优化资源利用并提升消息处理效率。以下是其核心机制与功能的详细解析&#xff1a; 一、Channel 的核心定义 虚拟通信链路 Channel 是建立在 TCP 连…

Zookeeper(80)Zookeeper的常见问题有哪些?

Zookeeper作为分布式系统的协调服务&#xff0c;常见的问题主要集中在配置、性能、连接管理、数据一致性和节点故障等方面。以下是一些常见问题及其详细解决方法和代码示例。 1. 配置问题 问题描述 配置不当可能导致 Zookeeper 集群无法正常启动或运行效率低下。 解决方法 …

如何管理路由器

一、管理路由器的必要性 1、需要修改拨号上网的密码。 2、需要修改WIFI的SSID名字和密码。 3、设置DHCP协议信息。 4、设置IP地址的过滤规则。 5、给某个设备连接设置网络限速。 二、常见的方式 (一)web网页方式 1、计算机用双绞线或者WIFI的方式连接路由器。 2、在计算机中打开…

linux vim 撤销 回退操作

在Linux的vim编辑器中&#xff0c;撤销和回退操作是非常基本的&#xff0c;但它们可以通过不同的方式实现&#xff0c;具体取决于你想要的精确效果。下面是一些常用的方法&#xff1a; 1. 撤销&#xff08;Undo&#xff09; 单个撤销&#xff1a; 你可以通过按下u键来撤销上一…

浅谈流媒体协议以及视频编解码

流媒体协议介绍 流媒体协议用于传输视频、音频等多媒体数据&#xff0c;确保数据流畅地传输到用户设备。常见的流媒体协议包括 RTMP、HLS、DASH、WebRTC 等&#xff0c;每种协议具有不同的特点和适用场景。 1. RTMP (Real-Time Messaging Protocol) 定义&#xff1a;由 Adob…

AF3 DataPipeline类process_multiseq_fasta 方法解读

AlphaFold3 data_pipeline 模块DataPipeline类的 process_multiseq_fasta 方法用于处理多序列 FASTA 文件,生成 AlphaFold3 结构预测所需的特征,适用于多链复合物的预测。它结合了 Minkyung Baek 在 Twitter 上提出的“AlphaFold-Gap”策略,即通过在多链 MSA 中插入固定长度…

图片爬取案例

修改前的代码 但是总显示“失败” 原因是 修改之后的代码 import requests import os from urllib.parse import unquote# 原始URL url https://cn.bing.com/images/search?viewdetailV2&ccidTnImuvQ0&id5AE65CE4BE05EE7A79A73EEFA37578E87AE19421&thidOIP.TnI…

使用自动化运维工具 Ansible 集中化管理服务器

一、概述 Ansible 是一款为类 Unix 系统开发的自由开源的配置和自动化工具 官方网站:https://www.ansible.com/ Ansible 成立于 2013 年,总部设在北卡罗来纳州达勒姆,联合创始人 ad Ziouani 和高级副总裁 Todd Barr都是红帽的老员工。Ansible 旗下的开源软件 Ansible 十分…

CMU15445(2023fall) Project #2 - Extendible Hash Index 匠心分析

胡未灭&#xff0c;鬓已秋&#xff0c;泪空流 此生谁料 心在天山 身老沧州 ——诉衷情 完整代码见&#xff1a; SnowLegend-star/CMU15445-2023fall: Having Conquered the Loftiest Peak, We Stand But a Step Away from Victory in This Stage. With unwavering determinati…

P1706 全排列问题

题目描述 按照字典序输出自然数 1 到 n 所有不重复的排列&#xff0c;即 n 的全排列&#xff0c;要求所产生的任一数字序列中不允许出现重复的数字。 输入格式 一个整数 n。 输出格式 由 1∼n 组成的所有不重复的数字序列&#xff0c;每行一个序列。 每个数字保留 5 个场宽。…

会话与会话管理:Cookie与Session的深度解析

一、什么是会话&#xff1f; 二、Cookie&#xff1a;客户端存储技术 1. Cookie的工作原理 2、在后端设置cookie 3、在前端设置cookie 三、浏览器开启了cookie禁用怎么办&#xff1f; 一、什么是会话&#xff1f; 会话&#xff08;Session&#xff09;是指一个用户与服务器之间…

【Linux系统】—— 冯诺依曼体系结构与操作系统初理解

【Linux系统】—— 冯诺依曼体系结构与操作系统初理解 1 冯诺依曼体系结构1.1 基本概念理解1.2 CPU只和内存打交道1.3 为什么冯诺依曼是这种结构1.4 理解数据流动 2 操作系统2.1 什么是操作系统2.2 设计OS的目的2.3 操作系统小知识点2.4 如何理解"管理"2.5 系统调用和…

算法-二叉树篇15-最大二叉树

最大二叉树 力扣题目链接 题目描述 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点&#xff0c;其值为 nums 中的最大值。 递归地在最大值 左边 的 子数组前缀上 构建左子树。 递归地在最大值 右边 的 子数组后缀上 构建…

运维Apache面试题及参考答案

目录 简述 Apache Web 服务器的主要特点及适用场景 Apache 的默认监听端口是什么?如何修改为其他端口? Apache 的主配置文件名称及路径是什么?不同 Linux 发行版的默认路径有何差异? 解释 Apache 的 MPM(Multi-Processing Module)机制,列举常见的工作模式(如 prefor…

51c自动驾驶~合集52

我自己的原文哦~ https://blog.51cto.com/whaosoft/13383340 #世界模型如何推演未来的千万种可能 驾驶世界模型&#xff08;DWM&#xff09;&#xff0c;专注于预测驾驶过程中的场景演变&#xff0c;已经成为追求自动驾驶的一种有前景的范式。这些方法使自动驾驶系统能够更…

用大白话解释缓存Redis +MongoDB是什么有什么用怎么用

Redis和MongoDB是什么&#xff1f; Redis&#xff1a;像你家的“小冰箱”&#xff0c;专门存高频使用的食物&#xff08;数据&#xff09;。它是基于内存的键值数据库&#xff0c;读写速度极快&#xff08;每秒超10万次操作&#xff09;。比如你每次打开手机App&#xff0c;用…

自然语言处理:词频-逆文档频率

介绍 大家好&#xff0c;博主又来给大家分享知识了。本来博主计划完成稠密向量表示的内容分享后&#xff0c;就开启自然语言处理中文本表示的讲解。可在整理分享资料的时候&#xff0c;博主发现还有个知识点&#xff0c;必须得单独拎出来好好说道说道。 这就是TF-IDF&#xf…

架构思维:架构的演进之路

文章目录 引言为什么架构思维如此重要架构师的特点软件架构的知识体系如何提升架构思维大型互联网系统架构的演进之路一、大型互联网系统的特点二、系统处理能力提升的两种途径三、大型互联网系统架构演化过程四、总结 引言 在软件开发行业中&#xff0c;有很多技术人可能会问…