前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >超90%应用都在用的限流算法!究竟是什么套路轻松应对突发流量过载?细品...

超90%应用都在用的限流算法!究竟是什么套路轻松应对突发流量过载?细品...

作者头像
程序视点
发布2024-06-07 14:52:04
740
发布2024-06-07 14:52:04
举报
文章被收录于专栏:程序小小事程序小小事
前言

前文,我们分享了限流算法中的固定窗口限流算法滑动窗口算法、漏桶限流算法及它们的实践。今天分享一个实践中最常用的限流算法:令牌桶算法。

令牌桶限流

算法介绍

令牌桶算法是实现限流的一种常见思路,用于限制请求的速率。它可以确保系统在高负载情况下仍能提供稳定的服务,并防止突发流量对系统造成过载。

最常用的 Google Java 开发工具包 Guava 中的限流工具类 RateLimiter 就是令牌桶的一个实现。令牌桶算法基于一个令牌桶的概念,其中令牌以固定的速率产生,并放入桶中。每个令牌代表一个请求的许可。当请求到达时,需要从令牌桶中获取一个令牌才能通过。如果令牌桶中没有足够的令牌,则请求被限制或丢弃。

令牌桶算法的实现步骤如下:

  1. 初始化一个令牌桶,包括桶的容量和令牌产生的速率。
  2. 持续以固定速率产生令牌,并放入令牌桶中,直到桶满为止。
  3. 当请求到达时,尝试从令牌桶中获取一个令牌。
  4. 如果令牌桶中有足够的令牌,则请求通过,并从令牌桶中移除一个令牌。
  5. 如果令牌桶中没有足够的令牌,则请求被限制或丢弃。

代码实现

代码语言:javascript
复制
package main

import (
   "fmt"
   "sync"
   "time"
)

// TokenBucket 表示一个令牌桶。
type TokenBucket struct {
   rate       float64    // 令牌添加到桶中的速率。
   capacity   float64    // 桶的最大容量。
   tokens     float64    // 当前桶中的令牌数量。
   lastUpdate time.Time  // 上次更新令牌数量的时间。
   mu         sync.Mutex // 互斥锁,确保线程安全。
}

// NewTokenBucket 创建一个新的令牌桶,给定令牌添加速率和桶的容量。
func NewTokenBucket(rate float64, capacity float64) *TokenBucket {
   return &TokenBucket{
      rate:       rate,
      capacity:   capacity,
      tokens:     capacity, // 初始时,桶是满的。
      lastUpdate: time.Now(),
   }
}

// Allow 检查是否可以从桶中移除一个令牌。如果可以,它移除一个令牌并返回 true。
// 如果不可以,它返回 false。
func (tb *TokenBucket) Allow() bool {
   tb.mu.Lock()
   defer tb.mu.Unlock()

   // 根据经过的时间计算要添加的令牌数量。
   now := time.Now()
   elapsed := now.Sub(tb.lastUpdate).Seconds() 
   tokensToAdd := elapsed * tb.rate            

   tb.tokens += tokensToAdd
   if tb.tokens > tb.capacity {
      tb.tokens = tb.capacity // 确保令牌数量不超过桶的容量。
   }

   if tb.tokens >= 1.0 {
      tb.tokens--
      tb.lastUpdate = now
      return true
   }

   return false
}

func main() {
   tokenBucket := NewTokenBucket(2.0, 3.0)

   for i := 1; i <= 10; i++ {
      now := time.Now().Format("15:04:05")
      if tokenBucket.Allow() {
         fmt.Printf(now+"  第 %d 个请求通过\n", i)
      } else { // 如果不能移除一个令牌,请求被拒绝。
         fmt.Printf(now+"  第 %d 个请求被限流\n", i)
      }
      time.Sleep(200 * time.Millisecond)
   }
}

执行结果:

优缺点

优点

  1. 平滑流量:令牌桶算法可以平滑突发流量,使得突发流量在一段时间内均匀地分布,避免了流量的突然高峰对系统的冲击。
  2. 灵活性:令牌桶算法可以通过调整令牌生成速率和桶的大小来灵活地控制流量。
  3. 允许突发流量:由于令牌桶可以积累一定数量的令牌,因此在流量突然增大时,如果桶中有足够的令牌,可以应对这种突发流量。

缺点

  1. 实现复杂:相比于其他一些限流算法(如漏桶算法),令牌桶算法的实现稍微复杂一些,需要维护令牌的生成和消耗。
  2. 需要精确的时间控制:令牌桶算法需要根据时间来生成令牌,因此需要有精确的时间控制。如果系统的时间控制不精确,可能会影响限流的效果。
  3. 可能会有资源浪费:如果系统的流量持续低于令牌生成的速率,那么桶中的令牌可能会一直积累,造成资源的浪费。

四种基本算法的对比

结合之前固定窗口限流算法滑动窗口算法、漏桶限流算法,这里把这几个算法做个对比和总结如下。

算法

优点

缺点

适合场景

固定窗口

简单直观,易于实现适用于稳定的流量控制易于实现速率控制

无法应对短时间内的突发流量流量不均匀时可能导致突发流量

稳定的流量控制,不需要保证请求均匀分布的场景

滑动窗口

平滑处理突发流量颗粒度更小,可以提供更精确的限流控制

实现相对复杂需要维护滑动窗口的状态存在较高的内存消耗

需要平滑处理突发流量的场景

漏桶算法

平滑处理突发流量可以固定输出速率,有效防止过载

对突发流量的处理不够灵活无法应对流量波动的情况

需要固定输出速率的场景避免流量的突然增大对系统的冲击的场景

令牌桶

平滑处理突发流量可以动态调整限流规则能适应不同时间段的流量变化

实现相对复杂需要维护令牌桶的状态

需要动态调整限流规则的场景需要平滑处理突发流量的场景

结各个算法的优缺点、适用场景上表都说得很清楚了。后续大家在项目中根据情况按需使用就好。

但以上不是限流算法的全部,随着微服务架构的普及,系统的服务通常会部署在多台服务器上,此时就需要分布式限流来保证整个系统的稳定性。后续我们将继续分享分布式限流的技术方案

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-05-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序视点 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档