令牌桶和漏桶是实现并发限流的两种经典算法。1. 令牌桶通过定时添加令牌、请求获取令牌执行,允许突发流量;2. 漏桶则以固定速率处理请求队列,严格控制流量。两者均可用go语言通过channel和定时器实现,适用于不同限流场景。

实现并发限流是高并发系统中常见的需求,Golang作为一门天然支持并发的语言,在这方面有很好的表现。其中,令牌桶和漏桶算法是比较经典的限流算法。下面将通过简单的示例来演示这两种算法的Go语言实现。

什么是令牌桶限流?
令牌桶(Token Bucket)是一种动态限流算法,其核心思想是:系统以固定速率向桶中添加令牌,请求只有在获取到令牌时才能被处理。如果桶满了,多余的令牌会被丢弃;如果没有令牌了,请求就会被拒绝或排队等待。

实现思路:
使用一个带缓冲的channel模拟令牌桶启动一个goroutine定时往channel中放入令牌每次请求尝试从channel中取出令牌,取不到则拒绝
package mainimport ( "fmt" "sync" "time")func tokenBucket(rps int) chan struct{} { tokens := make(chan struct{}, rps) // 初始填充令牌 for i := 0; i < rps; i++ { tokens <- struct{}{} } go func() { ticker := time.NewTicker(time.Second / time.Duration(rps)) defer ticker.Stop() for range ticker.C { select { case tokens <- struct{}{}: default: // 桶满就丢弃 } } }() return tokens}func main() { rate := 3 // 每秒允许3个请求 tokens := tokenBucket(rate) var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) go func(id int) { defer wg.Done() time.Sleep(time.Millisecond * 200) // 模拟并发请求 select { case <-tokens: fmt.Printf("请求 %d 被处理n", id) default: fmt.Printf("请求 %d 被限流n", id) } }(i) } wg.Wait()}
什么是漏桶限流?
漏桶(Leaky Bucket)算法则是另一种限流方式。它的原理是:所有请求先进入一个队列(桶),然后系统以固定速率从队列中取出请求处理。即使短时间内大量请求涌入,也只能按照固定的速率处理。
立即学习“go语言免费学习笔记(深入)”;
实现思路:
维护一个请求队列定时从队列中取出请求进行处理队列满了之后拒绝新请求
package mainimport ( "fmt" "sync" "time")type LeakyBucket struct { capacity int // 桶容量 rate time.Duration // 出水速率 queue chan struct{} wg sync.WaitGroup}func NewLeakyBucket(capacity int, rate time.Duration) *LeakyBucket { lb := &LeakyBucket{ capacity: capacity, rate: rate, queue: make(chan struct{}, capacity), } lb.startDraining() return lb}func (lb *LeakyBucket) startDraining() { go func() { ticker := time.NewTicker(lb.rate) defer ticker.Stop() for range ticker.C { select { case <-lb.queue: fmt.Println("处理一个请求") default: } } }()}func (lb *LeakyBucket) HandleRequest() bool { select { case lb.queue <- struct{}{}: return true default: return false }}func main() { bucket := NewLeakyBucket(5, time.Second/2) // 容量5,每秒处理2个请求 for i := 0; i < 10; i++ { time.Sleep(200 * time.Millisecond) if bucket.HandleRequest() { fmt.Printf("请求 %d 进入队列n", i) } else { fmt.Printf("请求 %d 被拒绝n", i) } } time.Sleep(6 * time.Second) // 等待漏桶处理完剩余请求}
如何选择令牌桶与漏桶?
两者虽然都能实现限流,但在行为上有所不同:
令牌桶更灵活,可以应对突发流量。比如设置桶容量为10,每秒放3个令牌,那瞬间最多能处理10个请求。漏桶限制更严格,强制请求按固定速率处理,适合需要平滑流量的场景。
常见使用建议:
如果你希望控制平均速率但允许短时间内的爆发请求,用令牌桶如果你需要严格控制输出速率、防止系统过载,用漏桶
小细节提醒
在实际生产中,可以结合
golang.org/x/time/rate
包中的
Limiter
来简化令牌桶实现注意channel大小要合理设置,避免内存浪费或频繁阻塞并发环境下注意锁机制,或者尽量使用无锁设计(如channel)
基本上就这些。两种算法都不复杂,但在实际应用中要注意根据业务特性做调整。
以上就是如何用Golang实现并发限流 演示令牌桶与漏桶算法的Go实现的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1398271.html
微信扫一扫
支付宝扫一扫