用go实现限流算法

文章目录

  • 固定窗口
      • 优缺点:
      • 适用场景:
      • 总结:
  • 滑动窗口
      • 优缺点:
      • 适用场景:
      • 总结:
  • 漏桶限流器
      • 优缺点:
      • 适用场景:
      • 总结:
  • 令牌桶
      • 优缺点:
      • 适用场景:
      • 总结:
  • 总结

今天总结一下四种限流算法,通过图形和代码的方式去一点点深入的探讨这些算法的一些本质,并且讲解以下优缺点。方便在以后的学习或者工作中,可以很好的去运用这些知识点。然后挑选最利于场景的一些算法。

固定窗口

在这里插入图片描述

优缺点:

优点

  • 简单易实现:固定窗口算法的逻辑简单,容易理解和实现。
  • 公平性:每个请求都有机会在窗口开始时被处理,避免了某些请求长时间等待的问题。

缺点

  • 临界点问题:在窗口切换的瞬间,可能会有大量请求同时到达,导致短时间内的请求量超过限制,这可能会对系统造成压力。
  • 不够灵活:窗口大小固定,无法根据实际流量动态调整。

适用场景:

  • 适用于请求频率相对稳定的场景:如果系统能够承受短时间内的高并发请求,固定窗口算法是一个不错的选择。
  • 适用于需要简单限流策略的场景:对于那些不需要复杂限流逻辑的系统,固定窗口算法提供了一个简单而有效的解决方案。

总结:

固定窗口限流算法是一种基础的限流策略,适用于需要简单限流控制的场景。然而,它可能不适合那些需要更平滑流量控制或需要根据实际流量动态调整限流策略的系统。在实际应用中,可能需要结合其他限流算法(如滑动窗口或令牌桶算法)来满足更复杂的业务需求。

package limiterimport ("sync""time"
)// FixedWindowLimiter 结构代表一个固定窗口限流器。
// 它使用固定大小的时间窗口来限制请求的数量。
type FixedWindowLimiter struct {limit    int           // 请求上限,窗口内允许的最大请求数window   time.Duration // 窗口时间大小,即时间窗口的长度counter  int           // 计数器,记录当前窗口内的请求数lastTime time.Time     // 上一次请求的时间mutex    sync.Mutex    // 互斥锁,用于同步,避免并发访问导致的问题
}// NewFixedWindowLimiter 构造函数创建并初始化一个新的 FixedWindowLimiter 实例。
func NewFixedWindowLimiter(limit int, window time.Duration) *FixedWindowLimiter {return &FixedWindowLimiter{limit:    limit,window:   window,lastTime: time.Now(), // 初始化时设置当前时间为窗口开始时间}
}// TryAcquire 尝试获取一个请求的机会。
// 如果当前窗口内请求数未达到上限,增加计数器并返回 true。
// 如果请求数已达到上限或窗口已过期,返回 false。
func (l *FixedWindowLimiter) TryAcquire() bool {l.mutex.Lock()defer l.mutex.Unlock()now := time.Now()// 检查当前时间与上次请求时间差是否超过窗口大小if now.Sub(l.lastTime) > l.window {l.counter = 0    // 如果窗口过期,重置计数器l.lastTime = now // 更新窗口开始时间为当前时间}// 如果当前请求数未达到上限,允许请求if l.counter < l.limit {l.counter++ // 请求成功,增加计数器return true // 返回 true 表示请求已成功获取}// 如果请求数已达到上限,请求失败return false
}

滑动窗口

[图片]

优缺点:

优点

  • 平滑处理请求:通过将大窗口划分为多个小窗口,可以更平滑地处理请求,避免固定窗口算法的临界点问题。
  • 灵活性:可以根据需要调整小窗口的大小和数量,以适应不同的流量模式。
  • 并发性能:使用读写锁允许多个并发读取操作,提高了并发性能。

缺点

  • 复杂性:相较于固定窗口算法,滑动窗口算法的实现和维护更复杂。
  • 内存****使用:需要存储每个小窗口的计数器,可能会占用更多的内存,尤其是在窗口数量很大时。

适用场景:

  • 适用于请求流量波动较大:如果系统需要处理不均匀的请求流量,滑动窗口算法可以更好地平滑流量峰值。
  • 适用于需要更精细控制:当需要根据实际流量动态调整限流策略时,滑动窗口算法提供了更高的灵活性。
  • 不适用于简单场景:对于简单的限流需求,固定窗口算法可能更简单、更易于实现。

总结:

滑动窗口限流算法是一种有效的流量控制机制,特别适合处理请求流量波动较大的场景。然而,它需要更多的计算和内存资源,因此在资源受限的环境中可能不是最佳选择。在实际应用中,需要根据具体的业务需求和系统资源来选择合适的限流算法。

package limiterimport ("errors""sync""time"
)// SlidingWindowLimiter 滑动窗口限流器,用于控制请求的速率。
type SlidingWindowLimiter struct {limit        int           // 窗口内允许的最大请求数window       int64         // 窗口时间大小(纳秒)smallWindow  int64         // 小窗口时间大小(纳秒)smallWindows int64         // 窗口内小窗口的数量counters     map[int64]int // 每个小窗口的请求计数mutex        sync.RWMutex  // 使用读写锁提高并发性能
}// NewSlidingWindowLimiter 创建并初始化滑动窗口限流器。
func NewSlidingWindowLimiter(limit int, window, smallWindow time.Duration) (*SlidingWindowLimiter, error) {if int64(window%smallWindow) != 0 {return nil, errors.New("window size must be divisible by the small window size")}return &SlidingWindowLimiter{limit:        limit,window:       int64(window),smallWindow:  int64(smallWindow),smallWindows: int64(window / smallWindow),counters:     make(map[int64]int),}, nil
}// TryAcquire 尝试在当前窗口内获取一个请求的机会。
func (l *SlidingWindowLimiter) TryAcquire() bool {l.mutex.RLock() // 读锁,允许多个并发读取defer l.mutex.RUnlock()now := time.Now().UnixNano()currentSmallWindow := now / l.smallWindow * l.smallWindow // 当前小窗口的起始点// 清理过期的小窗口计数器l.cleanExpiredWindows(now)// 检查并更新当前小窗口的计数l.mutex.Lock() // 写锁,更新计数器defer l.mutex.Unlock()count, exists := l.counters[currentSmallWindow]if !exists || count < l.limit {l.counters[currentSmallWindow] = count + 1return true}return false
}// cleanExpiredWindows 清理已过期的小窗口计数器。
func (l *SlidingWindowLimiter) cleanExpiredWindows(now int64) {startSmallWindow := now/l.smallWindow*l.smallWindow - l.windowfor smallWindow := range l.counters {if smallWindow < startSmallWindow {delete(l.counters, smallWindow)}}
}// 注意:cleanExpiredWindows 方法应该在持有读锁的情况下调用,以避免在遍历和修改计数器时产生竞态条件。

漏桶限流器

[图片]

[图片]

优缺点:

优点

  • 平滑处理请求:漏桶算法能够平滑处理请求,即使在流量突发的情况下也能保持稳定的处理速率。
  • 简单易实现:相对于其他复杂的限流算法,漏桶算法的实现较为简单。
  • 并发性能:使用读写锁允许多个并发读取操作,提高了并发性能。

缺点

  • 固定速率:漏桶算法以固定速率处理请求,可能不适合需要动态调整处理速率的场景。
  • 可能的延迟:在高流量情况下,请求可能需要等待桶内水位下降才能被处理,这可能导致延迟。

适用场景:

  • 适用于请求处理速率相对稳定的场景:例如,定时任务执行、消息队列处理等。
  • 适用于需要平滑流量的场景:例如,防止短时间内大量请求对系统造成压力。
  • 不适用于需要快速响应的场景:例如,实时性要求高的在线游戏或交易系统。

总结:

漏桶限流算法是一种有效的流量控制机制,特别适合处理请求速率相对稳定的服务。然而,它可能不适合那些需要快速响应或动态调整处理速率的系统。在实际应用中,需要根据具体的业务需求和系统资源来选择合适的限流算法。

package limiterimport ("errors""math""sync""time"
)// LeakyBucketLimiter 漏桶限流器
type LeakyBucketLimiter struct {peakLevel       int          // 最高水位currentLevel    int          // 当前水位currentVelocity int          // 水流速度/秒lastTime        time.Time    // 上次放水时间mutex           sync.RWMutex // 使用读写锁提高并发性能
}// NewLeakyBucketLimiter 初始化漏桶限流器
func NewLeakyBucketLimiter(peakLevel, currentVelocity int) (*LeakyBucketLimiter, error) {if currentVelocity <= 0 {return nil, errors.New("currentVelocity must be greater than 0")}if peakLevel < currentVelocity {return nil, errors.New("peakLevel must be greater than or equal to currentVelocity")}return &LeakyBucketLimiter{peakLevel:       peakLevel,currentLevel:    0, // 初始化时水位为0currentVelocity: currentVelocity,lastTime:        time.Now(),}, nil
}// TryAcquire 尝试获取处理请求的权限
func (l *LeakyBucketLimiter) TryAcquire() bool {l.mutex.RLock() // 读锁,允许多个并发读取defer l.mutex.RUnlock()// 如果上次放水时间距今不到1秒,不需要放水now := time.Now()interval := now.Sub(l.lastTime)l.mutex.Lock() // 写锁,更新水位defer l.mutex.Unlock()// 计算放水后的水位if interval >= time.Second {l.currentLevel = int(math.Max(0, float64(l.currentLevel)-(interval/time.Second).Seconds()*float64(l.currentVelocity)))l.lastTime = now}// 尝试增加水位if l.currentLevel < l.peakLevel {l.currentLevel++return true}return false
}

令牌桶

[图片]

[图片]

优缺点:

优点

  • 允许突发流量:令牌桶算法允许在桶中积累令牌,从而允许一定程度的突发流量。
  • 平滑流量:通过控制令牌的生成速率,可以平滑处理请求。
  • 简单易实现:算法实现相对简单,易于理解和维护。

缺点

  • 可能的延迟:在桶中没有足够令牌的情况下,请求需要等待令牌生成,可能导致延迟。
  • 固定速率:虽然允许突发流量,但令牌的生成速率是固定的,不够灵活。

适用场景:

  • 适用于需要平滑流量的场景:例如,网络流量控制、API 速率限制等。
  • 适用于允许一定程度突发流量的场景:例如,促销活动期间的流量突增。
  • 不适用于需要严格实时处理的场景:例如,实时交易系统,因为请求可能需要等待令牌生成。

总结:

令牌桶限流算法是一种有效的流量控制机制,特别适合处理需要平滑流量和允许一定程度突发流量的场景。然而,它可能不适合那些需要快速响应或动态调整处理速率的系统。在实际应用中,需要根据具体的业务需求和系统资源来选择合适的限流算法。

package limiterimport ("sync""time"
)// TokenBucketLimiter 令牌桶限流器
type TokenBucketLimiter struct {capacity      int        // 容量currentTokens int        // 令牌数量rate          int        // 发放令牌速率/秒lastTime      time.Time  // 上次发放令牌时间mutex         sync.Mutex // 避免并发问题
}// NewTokenBucketLimiter 创建一个新的令牌桶限流器实例。
func NewTokenBucketLimiter(capacity, rate int) *TokenBucketLimiter {return &TokenBucketLimiter{capacity:      capacity,rate:          rate,lastTime:      time.Now(),currentTokens: 0, // 初始化时桶中没有令牌}
}// TryAcquire 尝试从令牌桶中获取一个令牌。
func (l *TokenBucketLimiter) TryAcquire() bool {l.mutex.Lock()defer l.mutex.Unlock()now := time.Now()interval := now.Sub(l.lastTime) // 计算时间间隔// 如果距离上次发放令牌超过1秒,则发放新的令牌if interval >= time.Second {// 计算应该发放的令牌数量,但不超过桶的容量newTokens := int(interval/time.Second) * l.ratel.currentTokens = minInt(l.capacity, l.currentTokens+newTokens)// 更新上次发放令牌的时间l.lastTime = now}// 如果桶中没有令牌,则请求失败if l.currentTokens == 0 {return false}// 桶中有令牌,消费一个令牌l.currentTokens--return true
}// minInt 返回两个整数中的较小值。
func minInt(a, b int) int {if a < b {return a}return b
}package limiterimport ("sync""time"
)// TokenBucketLimiter 令牌桶限流器
type TokenBucketLimiter struct {capacity      int        // 容量currentTokens int        // 令牌数量rate          int        // 发放令牌速率/秒lastTime      time.Time  // 上次发放令牌时间mutex         sync.Mutex // 避免并发问题
}// NewTokenBucketLimiter 创建一个新的令牌桶限流器实例。
func NewTokenBucketLimiter(capacity, rate int) *TokenBucketLimiter {return &TokenBucketLimiter{capacity:      capacity,rate:          rate,lastTime:      time.Now(),currentTokens: 0, // 初始化时桶中没有令牌}
}// TryAcquire 尝试从令牌桶中获取一个令牌。
func (l *TokenBucketLimiter) TryAcquire() bool {l.mutex.Lock()defer l.mutex.Unlock()now := time.Now()interval := now.Sub(l.lastTime) // 计算时间间隔// 如果距离上次发放令牌超过1秒,则发放新的令牌if interval >= time.Second {// 计算应该发放的令牌数量,但不超过桶的容量newTokens := int(interval/time.Second) * l.ratel.currentTokens = minInt(l.capacity, l.currentTokens+newTokens)// 更新上次发放令牌的时间l.lastTime = now}// 如果桶中没有令牌,则请求失败if l.currentTokens == 0 {return false}// 桶中有令牌,消费一个令牌l.currentTokens--return true
}// minInt 返回两个整数中的较小值。
func minInt(a, b int) int {if a < b {return a}return b
}

总结

上面的就是对于四种限流算法的描述,可以通过其中的代码,还有一些具体的分析,利用流程图和模拟图的整体搭配,保证了学习效率

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

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

相关文章

【Python】Selenium怎么切换浏览器的页面

我们在爬网使用Selenium进行测试的时候&#xff0c;有时候想要点击浏览器里面的网址&#xff0c;跳到另一个页面上&#xff0c;获取第二个页面的内容。 可是有时候从官网进去&#xff0c;点击跳转到下一个页面以后&#xff0c;却没法定位到下一个页面的元素&#xff0c;这时候就…

Pytorch学习笔记day1—— 安装教程

这里写自定义目录标题 Pytorch安装方式 工作需要&#xff0c;最近开始搞一点AI的事情。但是这个国产的AI框架&#xff0c;实话说对初学者不太友好 https://www.mindspore.cn/ 比如说它不支持win下的CUDA&#xff0c;可是我手里只有3070Ti和4060也不太可能自己去买昇腾就有点绷不…

MongoDB教程(八):mongoDB数据备份与恢复

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; 文章目录 引言MongoDB 备…

01 机器学习概述

目录 1. 基本概念 2. 机器学习三要素 3. 参数估计的四个方法 3.1 经验风险最小化 3.2 结构风险最小化 3.3 最大似然估计 3.4 最大后验估计 4. 偏差-方差分解 5. 机器学习算法的类型 6. 数据的特征表示 7. 评价指标 1. 基本概念 机器学习&#xff08;Machine Le…

程控电阻器

程控电阻器 由于要测试电阻型温度传感器&#xff0c;一个电阻箱又很贵&#xff0c;就想做一款 程控电阻器 来满足。 设计满足300Ω到400kΩ可调电阻。 设计思路 选择数字电位器去控制电阻输出&#xff0c;最好是精度高&#xff0c;范围大的数字电位器。经过寻找后&#xff0c;发…

Beelzebub过程记录及工具集

文章目录 靶场搭建靶场测试过程安装dirsearch扫描目录wpscan扫描破解 靶场搭建 https://download.vulnhub.com/beelzebub/Beelzebub.zip 下载解压镜像&#xff0c;从vmware打开。 一键式开机即可。 打开后配置网络。 确保网络可达。 靶场测试过程 首先使用nmap扫描网段的存…

深入理解Session和Cookie的作用与联系

深入理解Session和Cookie的作用与联系 1、什么是Cookie&#xff1f;1、什么是Session&#xff1f;1、Session和Cookie的联系4、实际应用场景 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; Session和Cookie是两个至关重要的概念&#xff0c…

Abaqus基于CT断层扫描的三维重建插件CT2Model 3D

插件介绍 AbyssFish CT2Model 3D V1.0 插件可将采用X射线等方法获取的计算机断层扫描&#xff08;CT&#xff09;图像在Abaqus有限元软件内进行三维重建&#xff0c;进而高效获取可供模拟分析的有限元模型。插件可用于医学影像三维重构、混凝土细观三维重建、岩心数字化等领域…

商品运营分析

本文对某个品类&#xff08;猫砂&#xff09;在1688的情况&#xff0c;进行一定维度的分析&#xff1a; 内容主要是&#xff1a; 1.品类前景 2.阿里巴巴商家平台和淘宝平台销售&#xff0c;销量分析&#xff08;爬虫获取数据&#xff09; 3.对获取的数据&#xff0c;进行分…

解析 Mira :基于 Web3,让先进的 AI 技术易于访问和使用

“Mira 平台正在以 Web3 的方式解决当前 AI 开发面临的复杂性问题&#xff0c;同时保护 AI 贡献者的权益&#xff0c;让他们可以自主拥有并货币化自己的模型、数据和应用&#xff0c;以使先进的 AI 技术更加易于访问和使用。” AI 代表着一种先进的生产力&#xff0c;它通过深…

二叉树问题,两种解决方法(1遍历 2直接定义名字功能递归

1第一种方法就是另写一个traverse方法&#xff0c;2第二种方法就是把函数名当成已经实现的功能&#xff0c;直接写 1、翻转二叉树 class Solution {public TreeNode invertTree(TreeNode root) {if(rootnull) return null;TreeNode leftinvertTree(root.left);TreeNode righti…

博客都在使用的主题切换使用vue2实现思路

效果展示 步骤 1-变量定义css主题色 2-html初始化主题样式 3-vuex存储主题变量&#xff0c;点击触发修改根元素html的样式 4-method触发方法 mutation使用commit action使用dispatch 5-App组件引入该css文件&#xff0c;使用即可 6-将其加入本地存储&#xff0c;刷新后保持主…

烟雾监测与太阳能源:实验装置在其中的作用

太阳光在烟雾中的散射效应研究实验装置是一款模拟阳光透过烟雾环境的设备。此装置能帮助探究阳光在烟雾中的传播特性、散射特性及其对阳光的影响。 该装置主要包括光源单元、烟雾发生装置、光学组件、以及系统。光源单元负责产生类似于太阳光的光线&#xff0c;通常选用高亮度的…

华为OD算法题汇总

60、计算网络信号 题目 网络信号经过传递会逐层衰减&#xff0c;且遇到阻隔物无法直接穿透&#xff0c;在此情况下需要计算某个位置的网络信号值。注意:网络信号可以绕过阻隔物 array[m][n]&#xff0c;二维数组代表网格地图 array[i][j]0&#xff0c;代表i行j列是空旷位置 a…

Mamori.xyz:基于机器学习的区块链价值提取系统

Mamori.xyz 是一个基于机器学习的自动化区块链价值提取系统&#xff0c;其开创一种通用路径查找器&#xff0c;该工具可用于检测和防御潜在的未知安全风险&#xff0c;Mamori.xyz 也将其称为“未知的未知”&#xff0c;即智能合约中的零日漏洞和新出现的与区块链相关的软件问题…

leetcode-383.赎金信

题源 383.赎金信 题目描述 给你两个字符串&#xff1a;ransomNote 和 magazine &#xff0c;判断 ransomNote 能不能由 magazine 里面的字符构成。如果可以&#xff0c;返回 true &#xff1b;否则返回 false 。magazine 中的每个字符只能在 ransomNote 中使用一次。示例 1&…

Qt Creator:C++与Python混合编程

目录 1.前言 2.调用Python前的准备 3.在Qt Creator中配置Python库 4.在Qt Creator中添加Python代码 5.在Qt Creator中运行Python代码 6.运行效果 前言 在进行软件开发过程中&#xff0c;我们一般都是在特定的环境下特定的开发语言下进行编程。但是在开发中总有特殊情况&#xf…

微调 Florence-2 - 微软的尖端视觉语言模型

Florence-2 是微软于 2024 年 6 月发布的一个基础视觉语言模型。该模型极具吸引力&#xff0c;因为它尺寸很小 (0.2B 及 0.7B) 且在各种计算机视觉和视觉语言任务上表现出色。 Florence 开箱即用支持多种类型的任务&#xff0c;包括: 看图说话、目标检测、OCR 等等。虽然覆盖面…

前缀和算法——部分OJ题详解

&#xff08;文章的题目解释可能存在一些问题&#xff0c;欢迎各位小伙伴私信或评论指点&#xff08;双手合十&#xff09;&#xff09; 关于前缀和算法 前缀和算法解决的是“快速得出一个连续区间的和”&#xff0c;以前求区间和的时间复杂度是O(N)&#xff0c;使用前缀和可…

【精品资料】大数据可视化平台数据治理方案(626页WORD)

引言&#xff1a;大数据可视化平台的数据治理方案是一个综合性的策略&#xff0c;旨在确保大数据的质量、安全性、可访问性和合规性&#xff0c;从而支持高效的数据分析和可视化过程。 方案介绍&#xff1a; 大数据可视化平台的数据治理方案是一个综合性的策略&#xff0c;旨在…