185 lines
3.9 KiB
Go
185 lines
3.9 KiB
Go
|
|
package security
|
||
|
|
|
||
|
|
import (
|
||
|
|
"sync"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
// RateLimitAlgorithm 限流算法类型
|
||
|
|
type RateLimitAlgorithm string
|
||
|
|
|
||
|
|
const (
|
||
|
|
AlgorithmTokenBucket RateLimitAlgorithm = "token_bucket"
|
||
|
|
AlgorithmLeakyBucket RateLimitAlgorithm = "leaky_bucket"
|
||
|
|
AlgorithmSlidingWindow RateLimitAlgorithm = "sliding_window"
|
||
|
|
AlgorithmFixedWindow RateLimitAlgorithm = "fixed_window"
|
||
|
|
)
|
||
|
|
|
||
|
|
// TokenBucket 令牌桶算法
|
||
|
|
type TokenBucket struct {
|
||
|
|
capacity int64
|
||
|
|
tokens int64
|
||
|
|
rate int64 // 每秒产生的令牌数
|
||
|
|
lastRefill time.Time
|
||
|
|
mu sync.Mutex
|
||
|
|
}
|
||
|
|
|
||
|
|
// NewTokenBucket 创建令牌桶
|
||
|
|
func NewTokenBucket(capacity, rate int64) *TokenBucket {
|
||
|
|
return &TokenBucket{
|
||
|
|
capacity: capacity,
|
||
|
|
tokens: capacity,
|
||
|
|
rate: rate,
|
||
|
|
lastRefill: time.Now(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Allow 检查是否允许访问
|
||
|
|
func (tb *TokenBucket) Allow() bool {
|
||
|
|
tb.mu.Lock()
|
||
|
|
defer tb.mu.Unlock()
|
||
|
|
|
||
|
|
now := time.Now()
|
||
|
|
elapsed := now.Sub(tb.lastRefill).Seconds()
|
||
|
|
|
||
|
|
// 计算需要补充的令牌数
|
||
|
|
refillTokens := int64(elapsed * float64(tb.rate))
|
||
|
|
tb.tokens += refillTokens
|
||
|
|
if tb.tokens > tb.capacity {
|
||
|
|
tb.tokens = tb.capacity
|
||
|
|
}
|
||
|
|
tb.lastRefill = now
|
||
|
|
|
||
|
|
// 检查是否有足够的令牌
|
||
|
|
if tb.tokens > 0 {
|
||
|
|
tb.tokens--
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
|
||
|
|
// LeakyBucket 漏桶算法
|
||
|
|
type LeakyBucket struct {
|
||
|
|
capacity int64
|
||
|
|
water int64
|
||
|
|
rate int64 // 每秒漏出的水量
|
||
|
|
lastLeak time.Time
|
||
|
|
mu sync.Mutex
|
||
|
|
}
|
||
|
|
|
||
|
|
// NewLeakyBucket 创建漏桶
|
||
|
|
func NewLeakyBucket(capacity, rate int64) *LeakyBucket {
|
||
|
|
return &LeakyBucket{
|
||
|
|
capacity: capacity,
|
||
|
|
water: 0,
|
||
|
|
rate: rate,
|
||
|
|
lastLeak: time.Now(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Allow 检查是否允许访问
|
||
|
|
func (lb *LeakyBucket) Allow() bool {
|
||
|
|
lb.mu.Lock()
|
||
|
|
defer lb.mu.Unlock()
|
||
|
|
|
||
|
|
now := time.Now()
|
||
|
|
elapsed := now.Sub(lb.lastLeak).Seconds()
|
||
|
|
|
||
|
|
// 计算漏出的水量
|
||
|
|
leakWater := int64(elapsed * float64(lb.rate))
|
||
|
|
lb.water -= leakWater
|
||
|
|
if lb.water < 0 {
|
||
|
|
lb.water = 0
|
||
|
|
}
|
||
|
|
lb.lastLeak = now
|
||
|
|
|
||
|
|
// 检查桶是否已满
|
||
|
|
if lb.water < lb.capacity {
|
||
|
|
lb.water++
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
|
||
|
|
// SlidingWindow 滑动窗口算法
|
||
|
|
type SlidingWindow struct {
|
||
|
|
window time.Duration
|
||
|
|
capacity int64
|
||
|
|
requests []time.Time
|
||
|
|
mu sync.Mutex
|
||
|
|
}
|
||
|
|
|
||
|
|
// NewSlidingWindow 创建滑动窗口
|
||
|
|
func NewSlidingWindow(window time.Duration, capacity int64) *SlidingWindow {
|
||
|
|
return &SlidingWindow{
|
||
|
|
window: window,
|
||
|
|
capacity: capacity,
|
||
|
|
requests: make([]time.Time, 0),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Allow 检查是否允许访问
|
||
|
|
func (sw *SlidingWindow) Allow() bool {
|
||
|
|
sw.mu.Lock()
|
||
|
|
defer sw.mu.Unlock()
|
||
|
|
|
||
|
|
now := time.Now()
|
||
|
|
|
||
|
|
// 移除窗口外的请求
|
||
|
|
validRequests := make([]time.Time, 0)
|
||
|
|
for _, req := range sw.requests {
|
||
|
|
if now.Sub(req) < sw.window {
|
||
|
|
validRequests = append(validRequests, req)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
sw.requests = validRequests
|
||
|
|
|
||
|
|
// 检查是否超过容量
|
||
|
|
if int64(len(sw.requests)) < sw.capacity {
|
||
|
|
sw.requests = append(sw.requests, now)
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
|
||
|
|
// RateLimiter 限流器
|
||
|
|
type RateLimiter struct {
|
||
|
|
algorithm RateLimitAlgorithm
|
||
|
|
limiter interface{}
|
||
|
|
}
|
||
|
|
|
||
|
|
// NewRateLimiter 创建限流器
|
||
|
|
func NewRateLimiter(algorithm RateLimitAlgorithm, capacity, rate int64, window time.Duration) *RateLimiter {
|
||
|
|
limiter := &RateLimiter{algorithm: algorithm}
|
||
|
|
|
||
|
|
switch algorithm {
|
||
|
|
case AlgorithmTokenBucket:
|
||
|
|
limiter.limiter = NewTokenBucket(capacity, rate)
|
||
|
|
case AlgorithmLeakyBucket:
|
||
|
|
limiter.limiter = NewLeakyBucket(capacity, rate)
|
||
|
|
case AlgorithmSlidingWindow:
|
||
|
|
limiter.limiter = NewSlidingWindow(window, capacity)
|
||
|
|
default:
|
||
|
|
limiter.limiter = NewSlidingWindow(window, capacity)
|
||
|
|
}
|
||
|
|
|
||
|
|
return limiter
|
||
|
|
}
|
||
|
|
|
||
|
|
// Allow 检查是否允许访问
|
||
|
|
func (rl *RateLimiter) Allow() bool {
|
||
|
|
switch rl.algorithm {
|
||
|
|
case AlgorithmTokenBucket:
|
||
|
|
return rl.limiter.(*TokenBucket).Allow()
|
||
|
|
case AlgorithmLeakyBucket:
|
||
|
|
return rl.limiter.(*LeakyBucket).Allow()
|
||
|
|
case AlgorithmSlidingWindow:
|
||
|
|
return rl.limiter.(*SlidingWindow).Allow()
|
||
|
|
default:
|
||
|
|
return rl.limiter.(*SlidingWindow).Allow()
|
||
|
|
}
|
||
|
|
}
|