1. 漏桶算法
package leaky_bucket
import (
"fmt"
"time"
)
// 漏桶算法
// LeakyBucket 漏桶算法
type LeakyBucket struct {
queue chan struct{}
}
// NewLeakyBucket 创建一个漏桶算法
func NewLeakyBucket(bucketSize int) *LeakyBucket {
return &LeakyBucket{
queue: make(chan struct{}, bucketSize),
}
}
// Add 向漏桶中添加一个请求
func (l *LeakyBucket) Add() bool {
select {
case l.queue <- struct{}{}:
return true
default:
return false
}
}
// Remove 从漏桶中移除一个请求
func (l *LeakyBucket) Remove() {
for range l.queue {
fmt.Println("Request handled", time.Now().Format("2006-01-02 15:04:05"))
time.Sleep(100 * time.Millisecond)
}
}
func main() {
limiter := leaky_bucket.NewLeakyBucket(10)
go limiter.Remove() // 模拟处理请求
for i := 0; i < 15; i++ {
if limiter.Add() {
fmt.Println("Request", i+1, "allowed")
} else {
fmt.Println("Request", i+1, "rejected")
}
}
time.Sleep(time.Second * 5)
}
这段代码实现了一个简单的漏桶算法(Leaky Bucket Algorithm),用于流量控制。漏桶算法是一种经典的流量整形和限流算法,通过将请求放入一个固定容量的桶中,并以固定速率从桶中移除请求,来控制请求的速率。
1.1. 工作原理
-
初始化:
-
NewLeakyBucket
函数创建一个新的漏桶,接受一个参数bucketSize
,表示桶的容量。 -
使用
make(chan struct{}, bucketSize)
创建一个带缓冲的通道queue
,其缓冲区大小为bucketSize
。
-
-
添加请求:
-
Add
方法向漏桶中添加一个请求。 -
使用
select
语句尝试向queue
通道中发送一个空结构体struct{}{}
。如果通道未满,发送成功并返回true
;如果通道已满,发送失败并返回false
。
-
-
移除请求:
-
Remove
方法从漏桶中移除请求。 -
使用
for range
语句从queue
通道中读取请求,并处理每个请求,通道关闭的时候,循环会退出。处理请求的过程中,使用time.Sleep
模拟处理时间。
-
1.2. 优缺点
优点:
-
简单易懂:实现和理解相对简单,不需要复杂的数据结构。
-
恒定速率:通过固定的移除速率,能够平滑请求流量,防止突发流量。
-
防止过载:通过固定容量的桶,能够防止系统过载。
缺点:
-
固定处理速率:处理速率是固定的,无法动态调整,可能不适应瞬时高峰流量。
-
单一线程:当前实现中,
Remove
方法只能在单一线程中运行,无法利用多核处理能力。
2.1.3 改进方向
-
动态调整处理速率:可以根据系统负载,动态调整请求的处理速率。【可以加入time.NewTicker】
-
并发处理:使用多个 goroutine 来处理请求,提高处理能力。
package leaky_bucket
import (
"fmt"
"sync"
"time"
)
type LeakyBucketV2 struct {
queue chan struct{} // 漏桶队列
stopChan chan struct{} // 停止信号
wg sync.WaitGroup // 等待所有请求处理完成
}
// NewLeakyBucketV2 创建一个漏桶算法
func NewLeakyBucketV2(bucketSize int) *LeakyBucketV2 {
return &LeakyBucketV2{
queue: make(chan struct{}, bucketSize),
stopChan: make(chan struct{}),
}
}
// Add 向漏桶中添加一个请求
func (l *LeakyBucketV2) Add() bool {
select {
case l.queue <- struct{}{}:
return true
default:
return false
}
}
// Remove 从漏桶中移除一个请求
func (l *LeakyBucketV2) Remove() {
for {
select {
case <-l.queue:
l.wg.Add(1)
go func() {