优点:
缺点:
固定窗口限流算法是一种基础的限流策略,适用于需要简单限流控制的场景。然而,它可能不适合那些需要更平滑流量控制或需要根据实际流量动态调整限流策略的系统。在实际应用中,可能需要结合其他限流算法(如滑动窗口或令牌桶算法)来满足更复杂的业务需求。
package limiter import ( "sync" "time" ) // FixedWindowLimiter 结构代表一个固定窗口限流器。 // 它使用固定大小的时间窗口来限制请求的数量。 type FixedWindowLimiter struct { limit int // 请求上限,窗口内允许的最大请求数 window time.Duration // 窗口时间大小,即时间窗口的长度 counter int // 计数器,记录当前窗口内的请求数 lastTime time.Time // 上一次请求的时间 mutex sync.Mutex // 互斥锁,用于同步,避免并发访问导致的问题 } // NewFixedWindowLimiter 构造函数创建并初始化一个新的 FixedWindowLimiter 实例。 func NewFixedWindowLimiter(limit int, window time.Duration) *FixedWindowLimiter { return &FixedWindowLimiter{ limit: limit, window: window, lastTime: time.Now(), // 初始化时设置当前时间为窗口开始时间 } } // TryAcquire 尝试获取一个请求的机会。 // 如果当前窗口内请求数未达到上限,增加计数器并返回 true。 // 如果请求数已达到上限或窗口已过期,返回 false。 func (l *FixedWindowLimiter) TryAcquire() bool { l.mutex.Lock() defer l.mutex.Unlock() now := time.Now() // 检查当前时间与上次请求时间差是否超过窗口大小 if now.Sub(l.lastTime) > l.window { l.counter = 0 // 如果窗口过期,重置计数器 l.lastTime = now // 更新窗口开始时间为当前时间 } // 如果当前请求数未达到上限,允许请求 if l.counter < l.limit { l.counter++ // 请求成功,增加计数器 return true // 返回 true 表示请求已成功获取 } // 如果请求数已达到上限,请求失败 return false }
优点:
缺点:
滑动窗口限流算法是一种有效的流量控制机制,特别适合处理请求流量波动较大的场景。然而,它需要更多的计算和内存资源,因此在资源受限的环境中可能不是最佳选择。在实际应用中,需要根据具体的业务需求和系统资源来选择合适的限流算法。
package limiter import ( "errors" "sync" "time" ) // SlidingWindowLimiter 滑动窗口限流器,用于控制请求的速率。 type SlidingWindowLimiter struct { limit int // 窗口内允许的最大请求数 window int64 // 窗口时间大小(纳秒) smallWindow int64 // 小窗口时间大小(纳秒) smallWindows int64 // 窗口内小窗口的数量 counters map[int64]int // 每个小窗口的请求计数 mutex sync.RWMutex // 使用读写锁提高并发性能 } // NewSlidingWindowLimiter 创建并初始化滑动窗口限流器。 func NewSlidingWindowLimiter(limit int, window, smallWindow time.Duration) (*SlidingWindowLimiter, error) { if int64(window%smallWindow) != 0 { return nil, errors.New("window size must be divisible by the small window size") } return &SlidingWindowLimiter{ limit: limit, window: int64(window), smallWindow: int64(smallWindow), smallWindows: int64(window / smallWindow), counters: make(map[int64]int), }, nil } // TryAcquire 尝试在当前窗口内获取一个请求的机会。 func (l *SlidingWindowLimiter) TryAcquire() bool { l.mutex.RLock() // 读锁,允许多个并发读取 defer l.mutex.RUnlock() now := time.Now().UnixNano() currentSmallWindow := now / l.smallWindow * l.smallWindow // 当前小窗口的起始点 // 清理过期的小窗口计数器 l.cleanExpiredWindows(now) // 检查并更新当前小窗口的计数 l.mutex.Lock() // 写锁,更新计数器 defer l.mutex.Unlock() count, exists := l.counters[currentSmallWindow] if !exists || count < l.limit { l.counters[currentSmallWindow] = count + 1 return true } return false } // cleanExpiredWindows 清理已过期的小窗口计数器。 func (l *SlidingWindowLimiter) cleanExpiredWindows(now int64) { startSmallWindow := now/l.smallWindow*l.smallWindow - l.window for smallWindow := range l.counters { if smallWindow < startSmallWindow { delete(l.counters, smallWindow) } } } // 注意:cleanExpiredWindows 方法应该在持有读锁的情况下调用,以避免在遍历和修改计数器时产生竞态条件。
优点:
缺点:
漏桶限流算法是一种有效的流量控制机制,特别适合处理请求速率相对稳定的服务。然而,它可能不适合那些需要快速响应或动态调整处理速率的系统。在实际应用中,需要根据具体的业务需求和系统资源来选择合适的限流算法。
package limiter import ( "errors" "math" "sync" "time" ) // LeakyBucketLimiter 漏桶限流器 type LeakyBucketLimiter struct { peakLevel int // 最高水位 currentLevel int // 当前水位 currentVelocity int // 水流速度/秒 lastTime time.Time // 上次放水时间 mutex sync.RWMutex // 使用读写锁提高并发性能 } // NewLeakyBucketLimiter 初始化漏桶限流器 func NewLeakyBucketLimiter(peakLevel, currentVelocity int) (*LeakyBucketLimiter, error) { if currentVelocity <= 0 { return nil, errors.New("currentVelocity must be greater than 0") } if peakLevel < currentVelocity { return nil, errors.New("peakLevel must be greater than or equal to currentVelocity") } return &LeakyBucketLimiter{ peakLevel: peakLevel, currentLevel: 0, // 初始化时水位为0 currentVelocity: currentVelocity, lastTime: time.Now(), }, nil } // TryAcquire 尝试获取处理请求的权限 func (l *LeakyBucketLimiter) TryAcquire() bool { l.mutex.RLock() // 读锁,允许多个并发读取 defer l.mutex.RUnlock() // 如果上次放水时间距今不到1秒,不需要放水 now := time.Now() interval := now.Sub(l.lastTime) l.mutex.Lock() // 写锁,更新水位 defer l.mutex.Unlock() // 计算放水后的水位 if interval >= time.Second { l.currentLevel = int(math.Max(0, float64(l.currentLevel)-(interval/time.Second).Seconds()*float64(l.currentVelocity))) l.lastTime = now } // 尝试增加水位 if l.currentLevel < l.peakLevel { l.currentLevel++ return true } return false }
优点:
缺点:
令牌桶限流算法是一种有效的流量控制机制,特别适合处理需要平滑流量和允许一定程度突发流量的场景。然而,它可能不适合那些需要快速响应或动态调整处理速率的系统。在实际应用中,需要根据具体的业务需求和系统资源来选择合适的限流算法。
package limiter import ( "sync" "time" ) // TokenBucketLimiter 令牌桶限流器 type TokenBucketLimiter struct { capacity int // 容量 currentTokens int // 令牌数量 rate int // 发放令牌速率/秒 lastTime time.Time // 上次发放令牌时间 mutex sync.Mutex // 避免并发问题 } // NewTokenBucketLimiter 创建一个新的令牌桶限流器实例。 func NewTokenBucketLimiter(capacity, rate int) *TokenBucketLimiter { return &TokenBucketLimiter{ capacity: capacity, rate: rate, lastTime: time.Now(), currentTokens: 0, // 初始化时桶中没有令牌 } } // TryAcquire 尝试从令牌桶中获取一个令牌。 func (l *TokenBucketLimiter) TryAcquire() bool { l.mutex.Lock() defer l.mutex.Unlock() now := time.Now() interval := now.Sub(l.lastTime) // 计算时间间隔 // 如果距离上次发放令牌超过1秒,则发放新的令牌 if interval >= time.Second { // 计算应该发放的令牌数量,但不超过桶的容量 newTokens := int(interval/time.Second) * l.rate l.currentTokens = minInt(l.capacity, l.currentTokens+newTokens) // 更新上次发放令牌的时间 l.lastTime = now } // 如果桶中没有令牌,则请求失败 if l.currentTokens == 0 { return false } // 桶中有令牌,消费一个令牌 l.currentTokens-- return true } // minInt 返回两个整数中的较小值。 func minInt(a, b int) int { if a < b { return a } return b }package limiter import ( "sync" "time" ) // TokenBucketLimiter 令牌桶限流器 type TokenBucketLimiter struct { capacity int // 容量 currentTokens int // 令牌数量 rate int // 发放令牌速率/秒 lastTime time.Time // 上次发放令牌时间 mutex sync.Mutex // 避免并发问题 } // NewTokenBucketLimiter 创建一个新的令牌桶限流器实例。 func NewTokenBucketLimiter(capacity, rate int) *TokenBucketLimiter { return &TokenBucketLimiter{ capacity: capacity, rate: rate, lastTime: time.Now(), currentTokens: 0, // 初始化时桶中没有令牌 } } // TryAcquire 尝试从令牌桶中获取一个令牌。 func (l *TokenBucketLimiter) TryAcquire() bool { l.mutex.Lock() defer l.mutex.Unlock() now := time.Now() interval := now.Sub(l.lastTime) // 计算时间间隔 // 如果距离上次发放令牌超过1秒,则发放新的令牌 if interval >= time.Second { // 计算应该发放的令牌数量,但不超过桶的容量 newTokens := int(interval/time.Second) * l.rate l.currentTokens = minInt(l.capacity, l.currentTokens+newTokens) // 更新上次发放令牌的时间 l.lastTime = now } // 如果桶中没有令牌,则请求失败 if l.currentTokens == 0 { return false } // 桶中有令牌,消费一个令牌 l.currentTokens-- return true } // minInt 返回两个整数中的较小值。 func minInt(a, b int) int { if a < b { return a } return b }
上面的就是对于四种限流算法的描述,可以通过其中的代码,还有一些具体的分析,利用流程图和模拟图的整体搭配,保证了学习效率