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
}实现要点:
- 使用Redis有序集合存储时间戳
- Lua脚本保证原子操作
- 自动清理过期请求
- 精确统计时间窗口内请求
优势:
- 精确的滑动窗口计数
- 分布式系统通用
- 自动处理数据过期
三、高级方案:令牌桶算法
适用场景:允许突发流量、精细控制
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滑动窗口 | ★★★☆☆ | ★★★★★ | 中 | 是 |
| 令牌桶算法 | ★★★★☆ | ★★★★☆ | 允许突发 | 有限 |
| 限流中间件 | ★☆☆☆☆ | ★★★★☆ | 可配置 | 是 |
六、最佳实践
分层防御:
- 前端:按钮防重复点击
- 网关:基础IP限流
- 业务层:用户级精细控制
动态调整:
// 动态调整限流阈值 func adjustLimitBasedOnSystemLoad() { load := getSystemLoad() if load > 0.8 { limiter.SetMaxRequests(500) // 高负载时降低阈值 } }熔断机制:
// 使用hystrix实现熔断 hystrix.ConfigureCommand("my_api", hystrix.CommandConfig{ Timeout: 1000, MaxConcurrentRequests: 100, ErrorPercentThreshold: 50, })监控指标:
- 请求拒绝率
- 系统负载
- 限流阈值命中率
- Redis内存/QPS
总结
在Go语言中实现请求限流需要根据实际场景选择方案:
- 单机简单场景:计数器法
- 分布式系统:Redis滑动窗口
- 允许合理突发:令牌桶算法
- 快速上线:成熟中间件
黄金法则:没有最好的限流方案,只有最适合业务场景的方案。建议从简单实现开始,随着业务增长逐步升级限流策略,最终构建包含多层防御的完整限流体系。
到此这篇关于Go语言实现请求频率限制的方法实践的文章就介绍到这了,更多相关Go语言 请求频率限制内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
