[每周一更]-第92期:Go项目中的限流算法

在这里插入图片描述

这周五在清明假期内,提前更新文章

很多业务会有限流的场景,比如活动秒杀、社区搜索查询、社区留言功能;保护自身系统和下游系统不被巨型流量冲垮等。

在计算机网络中,限流就是控制网络接口发送或接收请求的速率,它可防止DoS攻击和限制Web爬虫。

限流,也称流量控制。是指系统在面临高并发,或者大流量请求的情况下,限制新的请求对系统的访问,从而保证系统的稳定性。限流会导致部分用户请求处理不及时或者被拒,这就影响了用户体验。所以一般需要在系统稳定和用户体验之间平衡一下。

一、场景

Go语言的限流场景非常广泛,适用于各种需要控制访问速率的场景。以下是一些常见的限流场景:

  • API服务限流: 假设你正在开发一个API服务,你可以使用限流来限制对API端点的访问速率,以防止恶意攻击或过度使用API。例如,你可以限制每个IP地址的请求速率,或者根据API密钥对用户进行限流。
  • Web爬虫控制: 如果你正在开发一个网络爬虫,你可能需要限制爬取网站的速率,以避免对目标网站造成过大的负担。你可以使用限流来控制爬取速率,以免被目标网站封禁IP地址。
  • 消息队列消费者: 在处理消息队列时,你可能需要控制消费者的处理速率,以防止过度消费消息。你可以使用限流来控制消费者从消息队列中拉取消息的速率,确保系统稳定运行。
  • 数据库访问限流: 在高并发的情况下,数据库可能成为瓶颈。你可以使用限流来控制对数据库的并发访问,以避免数据库超载或数据库连接池耗尽。
  • RPC调用限流: 如果你的应用程序依赖于其他服务的RPC调用,你可能需要限制对RPC服务的调用速率,以避免对目标服务造成过大的负荷或超出服务提供商的配额。
  • 任务调度: 控制任务调度器并发执行任务的速率,以避免过度消耗系统资源或超出服务协议。
  • 文件IO: 控制对文件系统的读写操作,以避免过度消耗系统资源。

二、常用算法

限流算法用于控制系统的流量,防止系统被过载。

  • 令牌桶算法(Token Bucket Algorithm): 令牌桶算法是一种基于令牌桶的限流算法。在这个算法中,有一个固定容量的桶,以固定的速率往桶里添加令牌。每当有请求到来时,必须从桶中获取一个令牌,如果桶中没有足够的令牌,则拒绝该请求或等待直到有足够的令牌为止。
  • 漏桶算法(Leaky Bucket Algorithm): 漏桶算法是一种基于漏桶的限流算法。在这个算法中,有一个固定容量的漏桶,以固定的速率漏水。每当有请求到来时,将请求放入漏桶中,如果漏桶已满,则拒绝该请求;否则,请求在漏桶中等待一段时间后被处理。
  • 计数器算法(Counter Algorithm)也叫固定窗口算法: 计数器算法是一种简单的限流算法。在这个算法中,统计一定时间窗口内的请求次数,如果请求次数超过了设定的阈值,则拒绝后续的请求。
  • 滑动窗口算法(Sliding Window Algorithm): 滑动窗口算法是一种动态调整窗口大小的限流算法。在这个算法中,统计一定时间窗口内的请求次数,如果请求次数超过了设定的阈值,则拒绝后续的请求。与计数器算法不同的是,滑动窗口算法可以动态调整时间窗口的大小,适应不同的流量变化。

以下看下各个算法的图及示例:

2.1、令牌桶算法

在这里插入图片描述

package mainimport ("time""golang.org/x/time/rate"
)func main() {// 创建一个令牌桶,每秒产生3个令牌limiter := rate.NewLimiter(3, 1)// 模拟10次请求for i := 0; i < 10; i++ {// 获取一个令牌,如果没有可用的令牌则阻塞等待limiter.WaitN(time.Now(), 1)// 处理请求handleRequest()}
}func handleRequest() {// 模拟处理请求println("Handling request...")
}
2.2、漏桶算法

在这里插入图片描述

package mainimport ("time"
)func main() {// 创建一个容量为3的漏桶,每秒漏水1个limiter := NewLeakyBucket(3, 1)// 模拟10次请求for i := 0; i < 10; i++ {// 获取一个漏桶令牌,如果漏桶已满则阻塞等待limiter.Wait()// 处理请求handleRequest()}
}func handleRequest() {// 模拟处理请求println("Handling request...")
}// 漏桶结构体
type LeakyBucket struct {capacity   int           // 漏桶容量rate       time.Duration // 漏桶速率lastLeak   time.Time     // 上一次漏水时间dripAmount int           // 漏水数量
}// 创建一个新的漏桶
func NewLeakyBucket(capacity int, ratePerSecond int) *LeakyBucket {return &LeakyBucket{capacity:   capacity,rate:       time.Second / time.Duration(ratePerSecond),lastLeak:   time.Now(),dripAmount: 0,}
}// 获取一个漏桶令牌,如果漏桶已满则阻塞等待
func (lb *LeakyBucket) Wait() {now := time.Now()// 计算自上一次漏水以来应该漏掉的数量lb.dripAmount += int(now.Sub(lb.lastLeak) / lb.rate)// 如果漏桶溢满,等待一段时间if lb.dripAmount > lb.capacity {time.Sleep(lb.rate)}// 更新上一次漏水时间lb.lastLeak = now// 漏水一个令牌lb.dripAmount--
}
2.3、计数器算法

在这里插入图片描述

package mainimport ("time"
)func main() {// 创建一个计数器限流器,每秒最多处理3个请求limiter := NewCounterLimiter(3, time.Second)// 模拟10次请求for i := 0; i < 10; i++ {// 判断是否允许进行请求if limiter.Allow() {// 处理请求handleRequest()} else {// 请求被限流,打印提示信息println("Request limited, please try again later.")}}
}func handleRequest() {// 模拟处理请求println("Handling request...")
}// 计数器限流器结构体
type CounterLimiter struct {counter    int           // 当前计数器值limit      int           // 计数器限制interval   time.Duration // 时间间隔lastUpdate time.Time     // 上次更新时间
}// 创建一个新的计数器限流器
func NewCounterLimiter(limit int, interval time.Duration) *CounterLimiter {return &CounterLimiter{counter:    0,limit:      limit,interval:   interval,lastUpdate: time.Now(),}
}// 判断是否允许进行请求
func (limiter *CounterLimiter) Allow() bool {// 更新计数器值limiter.updateCounter()// 判断计数器值是否超过限制if limiter.counter >= limiter.limit {return false}// 计数器值加1,表示处理一个请求limiter.counter++return true
}// 更新计数器值
func (limiter *CounterLimiter) updateCounter() {// 计算距离上次更新的时间间隔interval := time.Since(limiter.lastUpdate)// 如果时间间隔大于限定的间隔,则重置计数器if interval >= limiter.interval {limiter.counter = 0limiter.lastUpdate = time.Now()}
}
2.4、滑动窗口算法

在这里插入图片描述

package mainimport ("time"
)func main() {// 初始化一个滑动窗口限流器,窗口大小为1秒,允许的请求数为3limiter := NewSlidingWindowLimiter(3, 1*time.Second)// 模拟10次请求for i := 0; i < 10; i++ {// 判断是否允许进行请求,如果超过限制则等待for !limiter.Allow() {time.Sleep(time.Millisecond * 100)}// 处理请求handleRequest()}
}func handleRequest() {// 模拟处理请求println("Handling request...")
}// 滑动窗口限流器结构体
type SlidingWindowLimiter struct {requests []time.Time // 存储每个请求的时间戳limit    int         // 允许的请求数interval time.Duration // 时间窗口大小
}// 创建一个新的滑动窗口限流器
func NewSlidingWindowLimiter(limit int, interval time.Duration) *SlidingWindowLimiter {return &SlidingWindowLimiter{requests: make([]time.Time, 0),limit:    limit,interval: interval,}
}// 判断是否允许进行请求
func (limiter *SlidingWindowLimiter) Allow() bool {// 移除时间窗口外的请求for len(limiter.requests) > 0 && time.Since(limiter.requests[0]) > limiter.interval {limiter.requests = limiter.requests[1:]}// 如果请求数超过限制,则拒绝请求if len(limiter.requests) >= limiter.limit {return false}// 记录当前请求时间limiter.requests = append(limiter.requests, time.Now())return true
}

在Go语言中,可以使用一些库来实现限流,例如:

Go 在 x 标准库,即 golang.org/x/time/rate 里自带了一个限流器,这个限流器是基于令牌桶算法(token bucket)实现的。

  • github.com/juju/ratelimit:提供基于令牌桶算法的限流实现。
  • github.com/golang/groupcache:提供的并发访问控制工具可以用于限制对共享资源的并发访问。
  • github.com/uber-go/ratelimit:提供了令牌桶和漏桶算法的实现,支持更复杂的限流策略。

三、优缺点对比

这四种限流算法各有优缺点,简要的比较:

  1. 令牌桶算法:
    • 优点:
      • 令牌桶算法可以在令牌足够的情况下,突发地处理一定数量的请求。
      • 算法简单,容易实现。
    • 缺点:
      • 实现相对复杂,需要维护令牌桶的状态。
      • 无法应对突发流量,因为在令牌桶为空时无法处理任何请求。
  2. 漏桶算法:
    • 优点:
      • 漏桶算法可以对突发流量进行平滑处理,稳定输出。
      • 算法简单,容易实现。
    • 缺点:
      • 无法应对突发流量,因为漏桶已满时无法处理任何请求。
      • 对于突发流量的应对能力较弱。
  3. 计数器算法(固定窗口算法):
    • 优点:
      • 计数器算法简单直观,易于实现。
      • 可以精确控制单位时间内的请求量。
    • 缺点:
      • 对于突发流量的应对能力较弱,无法动态调整限流窗口。
      • 无法应对突发流量,因为在计数器达到限制后无法处理任何请求。
  4. 滑动窗口算法:
    • 优点:
      • 滑动窗口算法可以动态调整限流窗口的大小,适应不同的流量情况。
      • 对于突发流量的应对能力较强。
    • 缺点:
      • 算法相对复杂,实现较为困难。
      • 需要维护窗口中的请求记录,可能会消耗较多的内存。

四、参考

  • x_rate
  • go-zero_limiter示例+压测
  • (微服务)服务治理:几种开源限流算法库/应用软件介绍和使用
  • 令牌桶算法限流
  • 常见限流算法

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

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

相关文章

【算法练习】28:选择排序学习笔记

一、选择排序的算法思想 弄懂选择排序算法&#xff0c;先得知道两个概念&#xff1a;未排序序列&#xff0c;已排序序列。 原理&#xff1a;以升序为例&#xff0c;选择排序算法的思想是&#xff0c;先将整个序列当做未排序的序列&#xff0c;以序列的第一个元素开始。然后从左…

K8S - Deployment 的版本回滚

当前状态 先看deployment rootk8s-master:~# kubectl get deploy -o wide --show-labels NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES …

理解pytorch的广播语义

目录 什么是广播运算 广播的条件 示例 示例1 示例2 示例3 补1 示例4 原位运算 示例5 参与广播运算的两个tensor&#xff0c;必须是从右向左对齐 总结规律 两个tensor可以做广播运算的条件&#xff1a; 两个可以互相广播的tensor运算的步骤&#xff1a; 例子&#x…

【深度学习】深度学习md笔记总结第3篇:TensorFlow介绍,学习目标【附代码文档】

深度学习笔记完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;深度学习课程&#xff0c;深度学习介绍要求,目标,学习目标,1.1.1 区别,学习目标,学习目标。TensorFlow介绍&#xff0c;2.4 张量学习目标,2.4.1 张量(Tensor),2.4.2 创建张量的指令,2.4.3 张量…

整数与浮点数在内存中的存储

整数与浮点数在内存中的存储 一&#xff0c;大小端存储二&#xff0c;整数在内存中的存储三&#xff0c;浮点数在内存中的存储3.1浮点数的存储规则3.2浮点数的存储过程3.2.1有效数字M3.2.2指数E3.2.3浮点数存储的特殊情况4&#xff0c;例题讲解 在C语言的编程中&#xff0c;我们…

分布式系统架构中的相关概念

1.1、衡量网站的性能指标 响应时间&#xff1a;指执行一个请求从开始到最后收到响应数据所花费的总体时间。并发数&#xff1a;指系统同时能处理的请求数量。 并发连接数&#xff1a;指的是客户端向服务器发起请求&#xff0c;并建立了TCP连接。每秒钟服务器连接的总TCP数量请…

Python--Django--说明

Django 是基于python 的 Web 开发框架. &nsbp;   Web开发指的是开发基于B/S 架构, 通过前后端的配合, 将后台服务器上的数据在浏览器上展现给前台用户的应用. &nsbp;   在早期, 没有Web框架的时候, 使用 Python CGI 脚本显示数据库中的数据. Web框架致力于解决一些…

c++宏有什么离谱操作?

Boost.Preprocessor确实是一个非常强大而复杂的C宏库&#xff0c;专门用于元编程&#xff0c;即在编译时进行代码生成和变换。我这里有一套编程入门教程&#xff0c;不仅包含了详细的视频讲解&#xff0c;项目实战。如果你渴望学习编程不妨点个关注&#xff0c;给个评论222&…

面试总结------2024/04/04

1.面试官提问&#xff1a;你说你在项目中使用springsecurity jwt 实现了登录功能&#xff0c;能简单讲一下怎么实现的吗&#xff1f; 2.使用RabbitMQ实现订单超时取消功能 订单状态定义 首先&#xff0c;我们需要定义订单的不同状态。在这个示例中&#xff0c;我们可以定义以下…

实验一 Windows 2008虚拟机安装、安装VM Tools、快照和链接克隆、添加硬盘修改格式为GPT

一、安装vmware workstation软件 VMware workstation的安装介质&#xff0c;获取路径&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1AUAw_--yjZAUPbsR7StOJQ 提取码&#xff1a;umz1 所在目录&#xff1a;\vmware\VMware workstation 15.1.0 1.找到百度网盘中vmwa…

Pandas Dataframe合并连接Join和merge 参数讲解

文章目录 函数与参数分析otheronhowlsuffix, rsuffix, suffixesleft_index, right_index 函数与参数分析 在pandas中主要有两个函数可以完成table之间的join Join的函数如下&#xff1a; DataFrame.join(other, onNone, how‘left’, lsuffix‘’, rsuffix‘’, sortFalse, v…

YOLOv8 UI界面设计+热力图显示

进入可视化设计界面&#xff0c;设计UI pyside6-designer 设计好UI保存&#xff0c;然后通过以下命令将ui文件保存为py pyside6-uic myui.ui > myui.py 通过以下命令将资源文件qrc保存为py pyside6-rcc my_rc.qrc > my_rc.py 写主窗口函数实现功能... 项目基于yol…

基于Spring Boot的职称评审管理系统

基于Spring Boot的职称评审管理系统 开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/idea 部分系统展示 前台首页界面 用户注册登录界面 管理员登录界面 个人中心界面…

Driver not loaded之记录Qt访问MySql的解决经历

对于这个问题的本质原因&#xff0c;我也搞不明白&#xff0c;所以记录的方法不一定对所有人行之有效。我的目的很简单&#xff0c;就是把数据库用起来&#xff0c;经过查找网上资料&#xff0c;最终把数据库跑起来了。因此记录如下&#xff1a; 1&#xff0c;出现这个问题是缺…

【Go】十六、文件操作

文章目录 1、打开和关闭文件2、IO3、一次性读文件4、带缓冲区的读文件5、写入文件6、文件复制 1、打开和关闭文件 package main import("fmt""os" ) func main(){//打开文件&#xff1a;file,err : os.Open("d:/Test.txt");if err ! nil {//出错…

【医学影像数据处理】nii 数据格式文件操作汇总

大部分医学领域数据存储的都是dicom格式&#xff0c;但是对于CT等一类的序号图像&#xff0c;就需要多个dicom文件独立存储&#xff0c;最终构成一个序列series&#xff0c;这样存储就太过于复杂了。 nifti&#xff08;Neuroimaging Informatics Technology Initiative&#x…

GT收发器64B66B协议(2)自定义PHY设计

文章目录 前言一、设计框图二、GT_module三、PHY_module3.1、PHY_tx模块3.2、PHY_rx_bitsync模块3.3、PHY_rx模块 四、上板测试 前言 有了对64B66B协议的认识以及我们之前设计8B10B自定义PHY的经验&#xff0c;本文开始对64B66B自定义PHY的设计 一、设计框图 二、GT_module …

蓝桥杯单片机速成8-NE555频率测量

一、原理图 NOTE&#xff1a;使用NE555测量频率之前&#xff0c;需要将J3-15(SIGNAL)与J3-16(P34短接) 在使用矩阵键盘的时候也记得把跳冒拔下&#xff0c;因为有公共引脚P34 又是因为他的输出引脚是P34&#xff0c;所以只能用定时器0来作为计数器进行频率测量了 二、代码实现 …

CSS设置网页背景

目录 概述&#xff1a; 1.background-color: 2.background-image&#xff1a; 3.background-repeat&#xff1a; 4.background-position&#xff1a; 5.background-attachment&#xff1a; 6.background-size&#xff1a; 7.background-origin&#xff1a; 8.background-…

Linux初学(十四)LampLnmp

一、简介 LAMP和LNMP是两种常见的web服务器组合。具体如下&#xff1a; LAMP&#xff1a;LAMP代表的是Linux&#xff08;操作系统&#xff09; Apache&#xff08;HTTP服务器&#xff09; MySQL&#xff08;数据库&#xff09; PHP&#xff08;编程语言&#xff09;。这个组合被…