网站建设课程ppt,产品推荐词,游戏建模培训,seo搜索引擎优化期末考试这周五在清明假期内#xff0c;提前更新文章 很多业务会有限流的场景#xff0c;比如活动秒杀、社区搜索查询、社区留言功能#xff1b;保护自身系统和下游系统不被巨型流量冲垮等。
在计算机网络中#xff0c;限流就是控制网络接口发送或接收请求的速率#xff0c;它可防… 这周五在清明假期内提前更新文章 很多业务会有限流的场景比如活动秒杀、社区搜索查询、社区留言功能保护自身系统和下游系统不被巨型流量冲垮等。
在计算机网络中限流就是控制网络接口发送或接收请求的速率它可防止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 (timegolang.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.counterreturn 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提供了令牌桶和漏桶算法的实现支持更复杂的限流策略。
三、优缺点对比
这四种限流算法各有优缺点简要的比较
令牌桶算法 优点 令牌桶算法可以在令牌足够的情况下突发地处理一定数量的请求。算法简单容易实现。 缺点 实现相对复杂需要维护令牌桶的状态。无法应对突发流量因为在令牌桶为空时无法处理任何请求。 漏桶算法 优点 漏桶算法可以对突发流量进行平滑处理稳定输出。算法简单容易实现。 缺点 无法应对突发流量因为漏桶已满时无法处理任何请求。对于突发流量的应对能力较弱。 计数器算法固定窗口算法 优点 计数器算法简单直观易于实现。可以精确控制单位时间内的请求量。 缺点 对于突发流量的应对能力较弱无法动态调整限流窗口。无法应对突发流量因为在计数器达到限制后无法处理任何请求。 滑动窗口算法 优点 滑动窗口算法可以动态调整限流窗口的大小适应不同的流量情况。对于突发流量的应对能力较强。 缺点 算法相对复杂实现较为困难。需要维护窗口中的请求记录可能会消耗较多的内存。
四、参考
x_ratego-zero_limiter示例压测(微服务)服务治理几种开源限流算法库/应用软件介绍和使用 令牌桶算法限流常见限流算法