开发者

Go语言中的速率限流策略全面详解

开发者 https://www.devze.com 2023-11-30 10:41 出处:网络 作者: 低配全栈
目录引言1. 什么是速率限流2. 基本使用细节说明3. 动态调整限速器速率4. 应用场景1. API 请求限制2. 数据库访问控制3. 限制用户操作总结引言
目录
  • 引言
  • 1. 什么是速率限流
  • 2. 基本使用
    • 细节说明
  • 3. 动态调整限速器速率
    • 4. 应用场景
      • 1. API 请求限制
      • 2. 数据库访问控制
      • 3. 限制用户操作
    • 总结

      引言

      在前一篇文章中我们提到,为了防止缓存穿透,我们可以在应用里面的缓存层的上一层添加了singleflight。但是,我们也提到,即使使用了singleflight,我们一样还是会存在问题:

      使用singleflight本身并不会直接导致OOM的发生,但是需要考虑到极端情况下导致内存使用增加,如果有大量唯一的请求快速连续到来,并且它们的处理时间相对较长,则这些请求可能会在内存中积累,从而增加内存使用.

      什么,OpenAI又“雪崩”了?

      为了解决这种问题,今天在这里介绍一下GO语言中限流策略(Rating Limit)的使用。

      1. 什么是速率限流

      在 Go 语言中,Rate Limit(速率限制)是一种控制资源利用率的重要机制,尤其适用于控制对外部资源的访问速率,例如 API 请求或数据库操作。Go 标准库中的golang.org/x/time/rate包提供了实现速率限制的功能。

      核心概念

      •  Limiter(限制器): rate.Limiter 结构体是实现速率限制的主要组件。它使用令牌桶算法来控制事件发生的频率。

      • 令牌桶算法: 这是一种通过固定速率向桶中添加令牌来控制资源访问速率的算法。如果桶中有足够的令牌,请求就可以立即处理;如果没有,则请求需要等待或被拒绝。

      2. 基本使用

      首先看一个具体的例子:

      import (
        "context"
        "fmt"
        "log"
        "time"
        "golang.org/x/time/rate"
      )
      func main() {
        // 创建一个新的限制器,每秒产生5个令牌,最大桶容量为5
        limiter := rate.NewLimiter(5, 5)
        // 模拟连续请求
        for i := 0; i < 10; i++ {
          i := i
          go func() {
            // 等待下一个令牌
            err := limiter.Wait(context.Background())
            if err != nil {
              log.Fatal(err)
            }
            // 令牌已获取,执行API请求
            fmt.Println("Sending APIhttp://www.devze.com request", i, "at", time.Now().Format(time.RFC3339))
            // 这里可以添加执行实际 API 请求的代码
          }()
        }
        time.Sleep(10 * time.Second)
      }

      运行结果如下:

      Go语言中的速率限流策略全面详解

      我们可以看到,因为rate.Limit的限制,大约每1秒被限制发送5次请求。

      细节说明

      • 创建限制器: rate.NewLimiter(5, 5)&nbsp;创建了一个每秒生成 5 个令牌,最大桶容量为 5 的限制器。

      • 循环请求: 通过一个循环来模拟连续的 API 请求。

      • 等待令牌: 使用 limiter.Wait(context.Background()) 在每次请求之前等待令牌。这个调用会阻塞,直到获取到令牌为止。

      • 执行请求: 一旦获取到令牌,就打印一条消息表示发送了一个 API 请求。在实际应用中,这里可以替换为实际的 API 调用代码。

      3. 动态调整限速器速率

      在 Go 语言中,rate.Limiter 提供了动态调整速率的功能。通过这种方式i允许你在运行时根据需要改变速率限制,这在很多实际应用中非常有用,比如基于当前服务器负载或外部服务的可用性来调整请求速率。

      rate.Limiter 提供了 SetLimit 和 SetBurst 两个方法来动态调整限制器的速率和桶大小:

      • SetLimit(rate.Limit): 这个方法用来设置每秒可以生成的令牌数。rate.Limit 是一个基于浮点数的类型,用来表示每秒允许的事件数。

      • SetBurst(int): 这个方法用来设置限制器的桶大小。桶大小决定了在任何给定时间内限制器可以允许的最大事件数。

      package main
      import (
        "context"
        "fmt"
        "time"
        "golang.org/x/time/rate"
      )
      func main() {
        // 初始速率为每秒2个请求
        limiter := rate.NewLimiter(2, 2)
        // 模拟动态调整速率
        go func() {
          for {
            time.Sleep(5 * time.Second)
            // 每5秒动态调整速率和桶大小
            newRatephp := rate.Limit(float64(time.Now().Second()) / 10)
            limiter.SetLimit(newRate)
            limiter.SetBurst(int(newRate))
            fmt.Println("Rate updated to:", newRate)
          }
        }()
        // 模拟请求
        for i := 0; ; i++ {
          err := limiter.Wait(context.Background())
          if err != nil {
            fmt.Println("Error:", err)
            continue
          }
          fmt.Println("Request", i, "at", time.Now().Format(time.RFC3339))
        }
      }

      通过动态调整 rate.Limiter 的速率和桶大小,你可以灵活地控制应用中的速率限制,使其更加适应变化的环境和需求。上述例子中,每5s会更新速率,每次更新速率之后,请求数量可以实现动态调整:

      运行结果:

      Go语言中的速率限流策略全面详解

      4. 应用场景

      rate.Limiter 在 Go 语言中的应用场景广泛,主要用于控制资源的使用频率,以防止过载、滥用或达到限制。以下是一些常见的应用场景:

      1. API 请求限制

      当与外部服务(如 REST API)交互时python,通常会有每秒或每分钟的请求限制。使用 rate.Limiter 可以确保你的应用不会超过这些限制,从而避免触发服务端的速率限制错误或被暂时禁止访问。

      2. 数据库访问控制

      在高并发环境下,过多的数据库查询可能会导致性能下降或服务不可用。通过限制数据库操作的频率,可以减轻数据库的负载,提高应用的稳定性和响应速度。

      3. 限制用户操作

      在某些应用中,可能需要限制用户执行特定操作的频www.devze.com率,例如发送消息、提交表单或请求验证码。这有助于防止滥用和自动化攻击,同时保持系统资源的合理使用。

      总结

      rate.Limiter 的使用场景体现了其灵活性和实用性。无论是保护外部服务不被过载、控制资源访问、还是提高应用的整体稳定性,它都是一个非常有效的工具。在http://www.devze.com设计系统时,考虑到这些场景,合理地应用速率限制,可以显著提升系统的健壮性和用户体验。

      以上就是Go语言中的速率限流策略全面详解的详细内容,更多关于Go语言速率限流的资料请关注编程客栈(www.devze.com)其它相关文章!

      0

      精彩评论

      暂无评论...
      验证码 换一张
      取 消

      关注公众号