返回
深入解读: 常用限流算法在GO语言中的实现
后端
2023-10-23 01:10:23
**导读**
在现代分布式系统中, 限流算法作为一种关键技术, 被广泛应用于各种场景, 以保护系统免受过载和崩溃. 本文将深入探讨几种常用的限流算法, 并通过GO语言进行实现, 帮助读者理解这些算法的原理和应用.
**1. 计数器**
计数器是最简单的一种限流算法, 其原理是: 在一段时间间隔内, 对请求进行计数, 与阀值进行比较判断是否需要限流. 计数器算法的优点是简单易懂, 且容易实现. 但其缺点是: 无法处理突发流量, 且对慢速的持续请求过于严格.
```go
package main
import (
"fmt"
"sync"
"time"
)
// Counter 限流器
type Counter struct {
sync.Mutex
limit int
window time.Duration
count int
}
// NewCounter 新建限流器
func NewCounter(limit int, window time.Duration) *Counter {
return &Counter{
limit: limit,
window: window,
count: 0,
}
}
// Allow 判断是否允许通过
func (c *Counter) Allow() bool {
c.Lock()
defer c.Unlock()
if c.count < c.limit {
c.count++
return true
}
return false
}
// Reset 重置计数器
func (c *Counter) Reset() {
c.Lock()
defer c.Unlock()
c.count = 0
}
func main() {
// 创建限流器
c := NewCounter(10, 1*time.Second)
// 模拟请求
for i := 0; i < 20; i++ {
if c.Allow() {
fmt.Println("请求通过")
} else {
fmt.Println("请求被限流")
}
time.Sleep(100 * time.Millisecond)
}
}
2. 滑动窗口
滑动窗口算法是一种改进的计数器算法, 其原理是: 将时间窗口划分为多个时间片, 并在每个时间片内对请求进行计数. 当某一时间片的请求数超过阀值时, 则对该时间片内的请求进行限流. 滑动窗口算法可以较好地处理突发流量, 但其实现相对复杂.
package main
import (
"fmt"
"sync"
"time"
)
// SlidingWindow 限流器
type SlidingWindow struct {
sync.Mutex
limit int
window time.Duration
slots []int
index int
}
// NewSlidingWindow 新建限流器
func NewSlidingWindow(limit int, window time.Duration) *SlidingWindow {
slots := make([]int, int(window/time.Millisecond))
return &SlidingWindow{
limit: limit,
window: window,
slots: slots,
index: 0,
}
}
// Allow 判断是否允许通过
func (s *SlidingWindow) Allow() bool {
s.Lock()
defer s.Unlock()
// 更新当前槽位
s.slots[s.index] = 0
s.index = (s.index + 1) % len(s.slots)
// 计算当前窗口内的请求数
var count int
for _, slot := range s.slots {
count += slot
}
// 判断是否超过限流阈值
if count < s.limit {
s.slots[s.index]++
return true
}
return false
}
func main() {
// 创建限流器
s := NewSlidingWindow(10, 1*time.Second)
// 模拟请求
for i := 0; i < 20; i++ {
if s.Allow() {
fmt.Println("请求通过")
} else {
fmt.Println("请求被限流")
}
time.Sleep(100 * time.Millisecond)
}
}
3. 漏斗算法
漏斗算法是一种基于令牌桶算法的限流算法, 其原理是: 将令牌桶替换为漏斗, 令牌以恒定的速率流入漏斗, 当请求到达时, 从漏斗中取出令牌, 如果漏斗中没有令牌, 则对请求进行限流. 漏斗算法可以较好地处理突发流量, 且对慢速的持续请求更加宽容.
package main
import (
"fmt"
"sync"
"time"
)
// LeakyBucket 限流器
type LeakyBucket struct {
sync.Mutex
limit int
rate time.Duration
tokens int
}
// NewLeakyBucket 新建限流器
func NewLeakyBucket(limit int, rate time.Duration) *LeakyBucket {
return &LeakyBucket{
limit: limit,
rate: rate,
tokens: limit,
}
}
// Allow 判断是否允许通过