Golang

关注公众号 jb51net

关闭
首页 > 脚本专栏 > Golang > Go语言 请求频率限制

Go语言实现请求频率限制的方法实践

作者:码农老gou

本文介绍了Go语言中四种主流的请求限流方案,包括计数器法、Redis滑动窗口、令牌桶算法和使用成熟中间件,每种方案都有优缺点,适用于不同的场景,下面就来详细的介绍一下,感兴趣的可以了解一下

在实际开发中,接口被恶意刷请求是常见问题。本文将深入探讨Go语言中四种主流的请求限流方案,从简单到复杂逐步深入,助你构建高可用服务。

一、基础方案:计数器法(固定窗口)

适用场景:简单业务、低并发需求

type CounterLimiter struct {
	mu       sync.Mutex
	count    int
	interval time.Duration
	maxReq   int
	lastReset time.Time
}
func NewCounterLimiter(interval time.Duration, maxReq int) *CounterLimiter {
	return &CounterLimiter{
		interval:  interval,
		maxReq:    maxReq,
		lastReset: time.Now(),
	}
}
func (c *CounterLimiter) Allow() bool {
	c.mu.Lock()
	defer c.mu.Unlock()
	// 检查是否需要重置计数器
	if time.Since(c.lastReset) > c.interval {
		c.count = 0
		c.lastReset = time.Now()
	}
	// 检查是否超过限制
	if c.count >= c.maxReq {
		return false
	}
	c.count++
	return true
}
// HTTP中间件示例
func RateLimitMiddleware(limiter *CounterLimiter) func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if !limiter.Allow() {
				w.Header().Set("Retry-After", "60")
				http.Error(w, "Too Many Requests", http.StatusTooManyRequests)
				return
			}
			next.ServeHTTP(w, r)
		})
	}
}

优点

缺点

二、进阶方案:Redis滑动窗口

适用场景:分布式系统、精确限流

const luaScript = `
local key = KEYS[1]
local now = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local max = tonumber(ARGV[3])
redis.call('ZREMRANGEBYSCORE', key, 0, now - window)
local current = redis.call('ZCARD', key)
if current >= max then
    return 0
end
redis.call('ZADD', key, now, now)
redis.call('EXPIRE', key, math.ceil(window/1000))
return 1
`
type RedisLimiter struct {
	client   *redis.Client
	script   *redis.Script
	maxReq   int
	window   time.Duration
}
func NewRedisLimiter(client *redis.Client, window time.Duration, maxReq int) *RedisLimiter {
	return &RedisLimiter{
		client: client,
		script: redis.NewScript(luaScript),
		maxReq: maxReq,
		window: window,
	}
}
func (r *RedisLimiter) Allow(userID string) bool {
	ctx := context.Background()
	key := fmt.Sprintf("rate_limit:%s", userID)
	now := time.Now().UnixMilli()
	result, err := r.script.Run(ctx, r.client, 
		[]string{key}, now, r.window.Milliseconds(), r.maxReq).Int()
	return err == nil && result == 1
}

实现要点

  1. 使用Redis有序集合存储时间戳
  2. Lua脚本保证原子操作
  3. 自动清理过期请求
  4. 精确统计时间窗口内请求

优势

三、高级方案:令牌桶算法

适用场景:允许突发流量、精细控制

type TokenBucket struct {
	capacity    int           // 桶容量
	tokens      int           // 当前令牌数
	fillRate    time.Duration // 添加令牌间隔
	lastRefill  time.Time     // 上次添加时间
	mu          sync.Mutex
}
func NewTokenBucket(capacity int, rate time.Duration) *TokenBucket {
	return &TokenBucket{
		capacity:   capacity,
		tokens:     capacity,
		fillRate:   rate,
		lastRefill: time.Now(),
	}
}
func (b *TokenBucket) Allow() bool {
	b.mu.Lock()
	defer b.mu.Unlock()
	// 补充令牌
	now := time.Now()
	elapsed := now.Sub(b.lastRefill)
	newTokens := int(elapsed / b.fillRate)
	if newTokens > 0 {
		b.tokens += newTokens
		if b.tokens > b.capacity {
			b.tokens = b.capacity
		}
		b.lastRefill = now
	}
	// 检查令牌是否足够
	if b.tokens <= 0 {
		return false
	}
	b.tokens--
	return true
}

算法特点

四、生产级方案:使用成熟中间件

推荐库

Tollbooth示例

func main() {
	r := chi.NewRouter()
	
	// 创建限流器:每分钟1000次
	limiter := tollbooth.NewLimiter(1000/60.0, nil)
	limiter.SetIPLookups([]string{"X-Real-IP", "RemoteAddr", "X-Forwarded-For"})
	
	// 应用中间件
	r.Use(tollbooth_chi.LimitHandler(limiter))
	
	r.Get("/api/protected", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Protected content"))
	})
	
	http.ListenAndServe(":8080", r)
}

五、方案选型指南

方案实现复杂度精准度突发处理分布式支持
计数器法★☆☆☆☆★★☆☆☆
Redis滑动窗口★★★☆☆★★★★★
令牌桶算法★★★★☆★★★★☆允许突发有限
限流中间件★☆☆☆☆★★★★☆可配置

六、最佳实践

  1. 分层防御

    • 前端:按钮防重复点击
    • 网关:基础IP限流
    • 业务层:用户级精细控制
  2. 动态调整

    // 动态调整限流阈值
    func adjustLimitBasedOnSystemLoad() {
        load := getSystemLoad()
        if load > 0.8 {
            limiter.SetMaxRequests(500) // 高负载时降低阈值
        }
    }
  3. 熔断机制

    // 使用hystrix实现熔断
    hystrix.ConfigureCommand("my_api", hystrix.CommandConfig{
        Timeout:               1000,
        MaxConcurrentRequests: 100,
        ErrorPercentThreshold: 50,
    })
    
  4. 监控指标

    • 请求拒绝率
    • 系统负载
    • 限流阈值命中率
    • Redis内存/QPS

总结

在Go语言中实现请求限流需要根据实际场景选择方案:

黄金法则:没有最好的限流方案,只有最适合业务场景的方案。建议从简单实现开始,随着业务增长逐步升级限流策略,最终构建包含多层防御的完整限流体系。

到此这篇关于Go语言实现请求频率限制的方法实践的文章就介绍到这了,更多相关Go语言 请求频率限制内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文