返回

深入解读: 常用限流算法在GO语言中的实现

后端







**导读** 

在现代分布式系统中, 限流算法作为一种关键技术, 被广泛应用于各种场景, 以保护系统免受过载和崩溃. 本文将深入探讨几种常用的限流算法, 并通过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 判断是否允许通过