用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.rhkb.cn/news/380744.html

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

相关文章

SpringBoot结合ip2region实现博客评论显示IP属地

你好呀&#xff0c;我是小邹。 在现代的Web应用中&#xff0c;特别是博客和论坛类网站&#xff0c;为用户提供地理定位服务&#xff08;如显示用户所在地理位置&#xff09;可以极大地增强用户体验。本文将详细探讨如何使用Java和相关技术栈来实现在博客评论中显示用户的地址信…

NXP i.MX8系列平台开发讲解 - 3.19 Linux TTY子系统(二)

专栏文章目录传送门&#xff1a;返回专栏目录 Hi, 我是你们的老朋友&#xff0c;主要专注于嵌入式软件开发&#xff0c;有兴趣不要忘记点击关注【码思途远】 目录 1. Linux 串口驱动 1.1 Uart 驱动注册流程 1.2 uart 操作函数 1.3 line discipline 2. Linux tty应用层使用…

【 DHT11 温湿度传感器】使用STC89C51读取发送到串口、通过时序图编写C语言

文章目录 DHT11 温湿度传感器概述接线数据传送通讯过程时序图检测模块是否存在 代码实现总结对tmp tmp << 1;的理解对sendByte(datas[0]/10 0x30);的理解 DHT11 温湿度传感器 使用80C51单片机通过读取HDT11温湿度传感的数据&#xff0c;发送到串口。 通过时序图编写相应…

2024-07-18 Unity插件 Odin Inspector8 —— Type Specific Attributes

文章目录 1 说明2 特定类型特性2.1 AssetList2.2 AssetSelector2.3 ChildGameObjectsOnly2.4 ColorPalette2.5 DisplayAsString2.6 EnumPaging2.7 EnumToggleButtons2.8 FilePath2.9 FolderPath2.10 HideInInlineEditors2.11 HideInTables2.12 HideMonoScript2.13 HideReferenc…

STM32学习(3)--GPIO输入

GPIO输入 3.1GPIO输入1.按键介绍2.传感器模块介绍3.硬件电路4.C语言知识点补充&#xff08;1&#xff09;C语言数据类型&#xff08;2&#xff09;C语言宏定义&#xff08;3&#xff09;C语言typedef(4)C语言结构体&#xff08;5&#xff09;C语言枚举 3.2按键控制LED代码1.mai…

Python爬虫(基本流程)

1. 确定目标和范围 明确需求&#xff1a;确定你需要从哪些网站抓取哪些数据。合法性&#xff1a;检查目标网站的robots.txt文件&#xff0c;了解哪些内容可以被抓取。数据范围&#xff1a;确定爬取数据的起始和结束点&#xff0c;比如时间范围、页面数量等。 2. 选择合适的工…

深入理解Linux网络(二):UDP接收内核探究

深入理解Linux网络&#xff08;二&#xff09;&#xff1a;UDP接收内核探究 一、UDP 协议处理二、recvfrom 系统调⽤实现 一、UDP 协议处理 udp 协议的处理函数是 udp_rcv。 //file: net/ipv4/udp.c int udp_rcv(struct sk_buff *skb) {return __udp4_lib_rcv(skb, &udp_…

pyspark使用 graphframes创建和查询图的方法

1、安装graphframes的步骤 1.1 查看 spark 和 scala版本 在终端输入&#xff1a; spark-shell --version 查看spark 和scala版本 1.2 在maven库中下载对应版本的graphframes https://mvnrepository.com/artifact/graphframes/graphframes 我这里需要的是spark 2.4 scala 2.…

(南京观海微电子)——电感的电路原理及应用区别

电感 电感是导线内通过交流电流时&#xff0c;在导线的内部及其周围产生交变磁通&#xff0c;导线的磁通量与生产此磁通的电流之比。 当电感中通过直流电流时&#xff0c;其周围只呈现固定的磁力线&#xff0c;不随时间而变化&#xff1b;可是当在线圈中通过交流电流时&am…

linux内核中list的基本用法

内核链表 1 list_head 结构 为了使用链表机制&#xff0c;驱动程序需要包含<linux/types.h>头文件&#xff0c;该文件定义了如下结构体实现双向链&#xff1a; struct list_head {struct list_head *next, *prev; };2 链表的初始化 2.1 链表宏定义和初始化 可使用以…

智慧职校就业管理:开启校园招聘会新模式

在智慧职校的就业管理系统中&#xff0c;校园招聘会的出现&#xff0c;为学生们提供了一个展示自我、探寻职业道路的舞台&#xff0c;同时也为企业搭建了一座直面未来之星的桥梁。这一功能&#xff0c;凭借其独特的优势与前沿的技术&#xff0c;正在重新定义校园与职场之间的过…

react Jsx基础概念和本质

什么是jsx jsx是JavaScript和XML(HTML)的缩写&#xff0c;表示在js代码中编写HTML模板结构&#xff0c;它是react中编写UI模板的方式 const message this is message function App(){return (<div><h1>this is title</h1>{message}</div>) } jsx优…

【SpringBoot】 jasypt配置文件密码加解密

目前我们对yml配置文件中的密码都是明文显示&#xff0c;显然这不安全&#xff0c;有的程序员离职了以后可能会做一些非法骚操作&#xff0c;所以我们最好要做一个加密&#xff0c;只能让领导架构师或者技术经理知道这个密码。所以这节课就需要来实现一下。 我们可以使用jasypt…

持续集成08--Jenkins邮箱发送构建信息及测试报告

前言 在持续集成&#xff08;CI&#xff09;和持续部署&#xff08;CD&#xff09;的自动化流程中&#xff0c;及时通知团队成员关于构建的成功或失败是至关重要的。Jenkins&#xff0c;作为强大的CI/CD工具&#xff0c;提供了多种通知机制&#xff0c;其中邮件通知是最常用且有…

Java小技能:多级组织机构排序并返回树结构(包含每个层级的子节点和业务数据集合)

文章目录 引言I 实体定义1.1 部门1.2 用户组织机构中间表1.3 树状DTOII 抽取组织机构排序方法2.1 树状排序方法2.2 案例III 查询条件构建3.1 根据部门进行权限控制3.2 注入风险引言 需求: 根据组织机构进行数据授权控制,例如控制船舶、船舶设备、摄像头、港区查看权限。 一…

浅谈Canal原理

canal [kə’nl]&#xff0c;译意为水道/管道/沟渠&#xff0c;主要用途是基于 MySQL 数据库增量日志解析&#xff0c;提供增量数据 订阅 和 消费。应该是阿里云DTS&#xff08;Data Transfer Service&#xff09;的开源版本。 Canal与DTS提供的功能基本相似&#xff1a; 基于…

大模型实战—大模型赋能网络爬虫

大模型赋能网络爬虫 简单来说,网页抓取就是从网站抓取数据和内容,然后将这些数据保存为XML、Excel或SQL格式。除了用于生成潜在客户、监控竞争对手和市场研究外,网页抓取工具还可以用于自动化你的数据收集过程。 借助AI网页抓取工具,可以解决手动或纯基于代码的抓取工具的…

网络编程中的TCP和UDP

什么是TCP协议 TCP( Transmission control protocol )即传输控制协议&#xff0c;是一种面向连接、可靠的数据传输协议&#xff0c;它是为了在不可靠的互联网上提供可靠的端到端字节流而专门设计的一个传输协议。 面向连接 &#xff1a;数据传输之前客户端和服务器端必须建立连…

计算机体系结构||指令的调度和延迟分布(3)

实验3 指令的调度和延迟分布 3.1实验目的 &#xff08;1&#xff09;加深对指令调度技术的理解。 &#xff08;2&#xff09;加深对延迟分支技术的理解。 &#xff08;3&#xff09;熟练掌握用指令调度技术来解决流水线中的数据冲突的方法。 &#xff08;4&#xff09;进一…

最新版kubeadm搭建k8s(已成功搭建)

kubeadm搭建k8s&#xff08;已成功搭建&#xff09; 环境配置 主节点 k8s-master&#xff1a;4核8G、40GB硬盘、CentOS7.9&#xff08;内网IP&#xff1a;10.16.64.67&#xff09; 从节点 k8s-node1&#xff1a; 4核8G、40GB硬盘、CentOS7.9&#xff08;内网IP&#xff1a;10…