您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > Go语言

Go中的流量限制:有效控制流量

时间:2023-10-16 13:55:33  来源:微信公众号  作者:爱发白日梦的后端

介绍

速率限制是构建可扩展和弹性系统的关键技术。它通过对指定时间范围内允许的请求数量施加限制来帮助控制流量。在Go中实现速率限制可以确保最佳的资源利用,并保护您的应用程序免受过度流量或滥用行为的影响。在这篇博文中,我们将探索Go中的速率限制技术,并提供实用的代码示例来帮助您有效地实现它们。

理解速率限制

速率限制包括定义一组规则,这些规则决定客户端在给定的时间窗口内可以发出多少请求。确保系统能够处理负载,防止滥用或拒绝服务攻击。限制速率的两种常见方法是:

  • 固定窗口速率限制:在这种方法中,速率限制在固定的时间窗口内强制执行。例如,如果速率限制设置为每分钟100个请求,系统将在任何给定的60秒窗口中允许最多100个请求。超过此限制的请求将被拒绝或延迟到下一个时间窗口。
  • 令牌桶速率限制:令牌桶速率限制是基于从一个桶中消耗令牌的概念。桶最初由固定数量的令牌填充,每个令牌代表一个请求。当客户端想要发出请求时,它必须从桶中获取令牌。如果桶为空,客户端必须等待,直到令牌可用。

在GO中实现速率限制

Go提供了一个名为golang.org/x/time/rate的内置包,提供速率限制功能。让我们探讨一下如何同时使用固定窗口和令牌桶方法来实现速率限制。

固定窗口

func fixedWindowRateLimiting() {
 limiter := rate.NewLimiter(rate.Limit(100), 1) // 允许每秒100次

 for i := 0; i < 200; i++ {
  if !limiter.Allow() {
   fmt.Println("Rate limit exceeded. Request rejected.")
   continue
  }
  go process()
 }
}

// 处理请求
func process() {
 fmt.Println("Request processed successfully.")
 time.Sleep(time.Millisecond) // 模拟请求处理时间
}

在上面的代码片段中,我们创建了一个限制使用率。速率限制为每秒100个请求的NewLimiter。对每个请求调用limiter.Allow()方法,如果允许请求,则返回true;如果超过速率限制,则返回false。如果超过速率限制,请求将被拒绝。

对应的输出为,清楚的看到部分请求已经被拒绝了:

Request processed successfully.
Rate limit exceeded. Request rejected.
Rate limit exceeded. Request rejected.
Rate limit exceeded. Request rejected.
Rate limit exceeded. Request rejected.
Rate limit exceeded. Request rejected.
Rate limit exceeded. Request rejected.
Rate limit exceeded. Request rejected.
...

令牌桶

func tokenBucketRateLimiting() {
 limiter := rate.NewLimiter(rate.Limit(10), 5)
 ctx, _ := context.WithTimeout(context.TODO(), time.Millisecond)
 for i := 0; i < 200; i++ {
  if err := limiter.WAIt(ctx); err != nil {
   fmt.Println("Rate limit exceeded. Request rejected.")
   continue
  }
  go process()
 }
}

// 处理请求
func process() {
 fmt.Println("Request processed successfully.")
 time.Sleep(time.Millisecond) // 模拟请求处理时间
}

在上述代码中,我们使用 rate.NewLimiter 创建了一个限制器,其速率限制为每秒 10 个请求,突发 5 个请求。每个请求都会调用 limiter.Wait() 方法,该方法会阻塞直到有令牌可用。如果水桶是空的,没有可用的令牌,请求就会被拒绝。

对应的输出为,清楚的看到部分请求已经被拒绝了:

Request processed successfully.
Rate limit exceeded. Request rejected.
Rate limit exceeded. Request rejected.
Request processed successfully.
Rate limit exceeded. Request rejected.

动态速率限制

动态速率限制是指根据客户端行为、系统负载或业务规则等动态因素调整速率限制。这种技术允许您实时调整速率限制,以优化资源利用率并提供更好的用户体验。让我们看看 Go 中动态速率限制的示例:

func dynamicRateLimiting() {
 limiter := rate.NewLimiter(rate.Limit(10), 1) // 允许每秒100次

 // Dynamic rate adjustment
 go func() {
  time.Sleep(time.Second * 10) // 每10秒调整 limiter
  fmt.Println("---adjust limiter---")
  limiter.SetLimit(rate.Limit(200)) // 将 limiter 提升到每秒 200
 }()

 for i := 0; i < 3000; i++ {
  if !limiter.Allow() {
   fmt.Println("Rate limit exceeded. Request rejected.")
   time.Sleep(time.Millisecond * 100)
   continue
  }
  process()
 }
}

// 处理请求
func process() {
 fmt.Println("Request processed successfully.")
 time.Sleep(time.Millisecond * 10) // 模拟请求处理时间
}

在上面的代码片段中,我们创建了一个限制器,初始速率限制为每秒 10 个请求。然后,我们启动一个 goroutine,在 10秒钟后将速率限制调整为每秒 200 个请求。这样,我们就能根据不断变化的情况动态调整速率限制。

对应的输出为,这里我们可以看到中途的时候,某些请求已经被拒绝掉了,后来我们通过动态调整,后续的请求都可以正常通过了:

...
Request processed successfully.
Rate limit exceeded. Request rejected.
Request processed successfully.
---adjust limiter---
Rate limit exceeded. Request rejected.
Request processed successfully.
Request processed successfully.
Request processed successfully.
Request processed successfully.
Request processed successfully.
...

自适应速率限制

自适应速率限制可根据之前请求的响应时间或错误率动态调整速率限制。它允许系统自动适应不同的流量条件,确保最佳性能和资源利用率。让我们看看 Go 中自适应速率限制的示例:

func adaptiveRateLimiting() {
 limiter := rate.NewLimiter(rate.Limit(10), 1) // 允许每秒10次

 // 自适应调整
 go func() {
  for {
   time.Sleep(time.Second * 10)
   responseTime := measureResponseTime() // 测量之前请求的响应时间
   if responseTime > 500*time.Millisecond {
    fmt.Println("---adjust limiter 50---")
    limiter.SetLimit(rate.Limit(50))
   } else {
    fmt.Println("---adjust limiter 100---")
    limiter.SetLimit(rate.Limit(100))
   }
  }
 }()

 for i := 0; i < 3000; i++ {
  if !limiter.Allow() {
   fmt.Println("Rate limit exceeded. Request rejected.")
   time.Sleep(time.Millisecond * 100)
   continue
  }
  process()
 }
}

// 测量以前请求的响应时间
// 执行自己的逻辑来测量响应时间
func measureResponseTime() time.Duration {
 return time.Millisecond * 100
}

// 处理请求
func process() {
 fmt.Println("Request processed successfully.")
 time.Sleep(time.Millisecond * 10) // 模拟请求处理时间
}

在上述代码片段中,我们使用 measureResponseTime 函数模拟测量之前请求的响应时间。根据测量到的响应时间,我们通过使用 limiter.SetLimit 设置不同的值来动态调整速率限制。这样,系统就能根据观察到的响应时间调整其速率限制策略。

对应的输出为:

Request processed successfully.
Rate limit exceeded. Request rejected.
Request processed successfully.
Rate limit exceeded. Request rejected.
---adjust limiter 100---
Request processed successfully.
Request processed successfully.
Request processed successfully.
Request processed successfully.
Request processed successfully.

总结

速率限制是维护 Go 应用程序稳定性和安全性的基本技术。通过有效控制传入请求的流量,您可以防止资源耗尽并确保资源的公平分配。在这篇博文中,我们探讨了固定窗口和令牌桶速率限制的概念,并提供了代码片段,演示如何使用 golang.org/x/time/rate 包在 Go 中实现它们。将速率限制纳入您的应用程序,以构建能够高效处理不同流量水平的弹性系统。

当然可以使用第三方库来实现,比如说:go.uber.org/ratelimit



Tags:Go   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
机械设备B2B工厂外贸网站如何做Google谷歌SEO优化关键字排名?
在今天的全球化市场中,机械设备行业正面临着激烈的竞争。要在这一领域脱颖而出,拥有一个优化良好的B2B外贸网站至关重要。通过精准的谷歌SEO关键字排名,您的网站可以吸引更多海...【详细内容】
2024-04-08  Search: Go  点击:(4)  评论:(0)  加入收藏
Google搜索引擎索引的网页数量有多少?谷歌官方提供数据进行参考
Google搜索引擎索引的网页数量有多少?二十世纪九十年代,网页的索引数量成了一个各大搜索引擎相互对比的指标。小编记得2000年谷歌搜索引擎的首页搜索框上方,还标记着谷歌索引的...【详细内容】
2024-03-27  Search: Go  点击:(12)  评论:(0)  加入收藏
在Java应用程序中释放峰值性能:配置文件引导优化(PGO)概述
译者 | 李睿审校 | 重楼在Java开发领域,优化应用程序的性能是开发人员的持续追求。配置文件引导优化(Profile-Guided Optimization,PGO)是一种功能强大的技术,能够显著地提高Ja...【详细内容】
2024-03-18  Search: Go  点击:(24)  评论:(0)  加入收藏
宝藏级Go语言开源项目——教你自己动手开发互联网搜索引擎
DIYSearchEngine 是一个能够高速采集海量互联网数据的开源搜索引擎,采用 Go 语言开发。Github 地址:https://github.com/johnlui/DIYSearchEngine运行方法首先,给自己准备一杯...【详细内容】
2024-03-12  Search: Go  点击:(18)  评论:(0)  加入收藏
Go Gin框架实现优雅地重启和停止
在Web应用程序中,有时候我们需要重启或停止服务器,无论是因为更新代码还是进行例行维护。在这种情景下,我们需要保证应用程序的可用性和数据的一致性。这就需要优雅地关闭和重...【详细内容】
2024-01-30  Search: Go  点击:(67)  评论:(0)  加入收藏
如何让Go程序以后台进程或daemon方式运行
本文探讨了如何通过Go代码实现在后台运行的程序。最近我用Go语言开发了一个WebSocket服务,我希望它能在后台运行,并在异常退出时自动重新启动。我的整体思路是将程序转为后台...【详细内容】
2024-01-26  Search: Go  点击:(60)  评论:(0)  加入收藏
深入Go底层原理,重写Redis中间件实战
Go语言以其简洁、高效和并发性能而闻名,深入了解其底层原理可以帮助我们更好地利用其优势。在本文中,我们将探讨如何深入Go底层原理,以及如何利用这些知识重新实现一个简单的Re...【详细内容】
2024-01-25  Search: Go  点击:(66)  评论:(0)  加入收藏
支付宝宣布更换Logo
鞭牛士 1月19日消息,今日,支付宝宣布更新Logo,此次最大的变化在于去掉了外框与文字,仅保留最具辨识度的“支”字标识。据了解,这是支付宝时隔4年再次更换Logo。支付宝App目前已用...【详细内容】
2024-01-19  Search: Go  点击:(71)  评论:(0)  加入收藏
Go 内存优化与垃圾收集
Go提供了自动化的内存管理机制,但在某些情况下需要更精细的微调从而避免发生OOM错误。本文将讨论Go的垃圾收集器、应用程序内存优化以及如何防止OOM(Out-Of-Memory)错误。Go...【详细内容】
2024-01-15  Search: Go  点击:(61)  评论:(0)  加入收藏
Go函数指针是如何让你的程序变慢的?
导读Go 语言的常规优化手段无需赘述,相信大家也能找到大量的经典教程。但基于 Go 的函数值问题,业界还没有太多深度讨论的内容分享。本文作者根据自己对 Go 代码的使用与调优...【详细内容】
2024-01-15  Search: Go  点击:(85)  评论:(0)  加入收藏
▌简易百科推荐
宝藏级Go语言开源项目——教你自己动手开发互联网搜索引擎
DIYSearchEngine 是一个能够高速采集海量互联网数据的开源搜索引擎,采用 Go 语言开发。Github 地址:https://github.com/johnlui/DIYSearchEngine运行方法首先,给自己准备一杯...【详细内容】
2024-03-12  OSC开源社区    Tags:Go语言   点击:(18)  评论:(0)  加入收藏
Go Gin框架实现优雅地重启和停止
在Web应用程序中,有时候我们需要重启或停止服务器,无论是因为更新代码还是进行例行维护。在这种情景下,我们需要保证应用程序的可用性和数据的一致性。这就需要优雅地关闭和重...【详细内容】
2024-01-30  源自开发者  微信公众号  Tags:Go   点击:(67)  评论:(0)  加入收藏
如何让Go程序以后台进程或daemon方式运行
本文探讨了如何通过Go代码实现在后台运行的程序。最近我用Go语言开发了一个WebSocket服务,我希望它能在后台运行,并在异常退出时自动重新启动。我的整体思路是将程序转为后台...【详细内容】
2024-01-26  Go语言圈  微信公众号  Tags:Go程序   点击:(60)  评论:(0)  加入收藏
深入Go底层原理,重写Redis中间件实战
Go语言以其简洁、高效和并发性能而闻名,深入了解其底层原理可以帮助我们更好地利用其优势。在本文中,我们将探讨如何深入Go底层原理,以及如何利用这些知识重新实现一个简单的Re...【详细内容】
2024-01-25  547蓝色星球    Tags:Go   点击:(66)  评论:(0)  加入收藏
Go 内存优化与垃圾收集
Go提供了自动化的内存管理机制,但在某些情况下需要更精细的微调从而避免发生OOM错误。本文将讨论Go的垃圾收集器、应用程序内存优化以及如何防止OOM(Out-Of-Memory)错误。Go...【详细内容】
2024-01-15  DeepNoMind  微信公众号  Tags:Go   点击:(61)  评论:(0)  加入收藏
Go函数指针是如何让你的程序变慢的?
导读Go 语言的常规优化手段无需赘述,相信大家也能找到大量的经典教程。但基于 Go 的函数值问题,业界还没有太多深度讨论的内容分享。本文作者根据自己对 Go 代码的使用与调优...【详细内容】
2024-01-15  腾讯云开发者  微信公众号  Tags:Go函数   点击:(85)  评论:(0)  加入收藏
Go编程中调用外部命令的几种场景
在很多场合, 使用Go语言需要调用外部命令来完成一些特定的任务, 例如: 使用Go语言调用Linux命令来获取执行的结果,又或者调用第三方程序执行来完成额外的任务。在go的标准库...【详细内容】
2024-01-09  suntiger    Tags:Go编程   点击:(100)  评论:(0)  加入收藏
Go 语言不支持并发读写 Map,为什么?
Go语言的map类型不支持并发读写的主要原因是并发读写会导致数据竞态(data race),这意味着多个 goroutine 可能同时访问并修改同一个 map,从而引发不确定的结果。在Go语言的设计...【详细内容】
2024-01-05  Go语言圈  微信公众号  Tags:Go 语言   点击:(77)  评论:(0)  加入收藏
Go微服务入门到容器化实践
Go微服务入门到容器化实践Go 是一门高效、现代化、快速增长的编程语言,非常适合构建 Web 应用程序。而 Docker 是一种轻量级的容器化技术,能够使得您的应用程序在任何地方运行...【详细内容】
2024-01-01  大雷家吃饭    Tags:Go微服务   点击:(61)  评论:(0)  加入收藏
你是否想知道如何应对高并发?Go语言为你提供了答案!
并发编程是当前软件领域中不可忽视的一个关键概念。随着CPU等硬件的不断发展,我们都渴望让我们的程序运行速度更快、更快。而Go语言在语言层面天生支持并发,充分利用现代CPU的...【详细内容】
2023-12-29  灵墨AI探索室  微信公众号  Tags:Go语言   点击:(107)  评论:(0)  加入收藏
站内最新
站内热门
站内头条