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

 更新时间:2026年04月19日 09:58:36   作者:码农老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语言中实现请求限流需要根据实际场景选择方案:

  • 单机简单场景:计数器法
  • 分布式系统:Redis滑动窗口
  • 允许合理突发:令牌桶算法
  • 快速上线:成熟中间件

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

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

相关文章

  • Go语言开发k8s之Deployment操作解析

    Go语言开发k8s之Deployment操作解析

    这篇文章主要为大家介绍了Go语言开发k8s之Deployment操作解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-06-06
  • Go实现整合Logrus实现日志打印

    Go实现整合Logrus实现日志打印

    这篇文章主要介绍了Go实现整合Logrus实现日志打印,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-07-07
  • 深入了解Go语言的基本语法与常用函数

    深入了解Go语言的基本语法与常用函数

    这篇文章主要为大家详细介绍一下Go语言中的基本语法与常用函数,文中的示例代码讲解详细,对我们学习Go语言有一定的帮助,需要的可以参考一下
    2022-07-07
  • Golang中匿名函数的实现

    Golang中匿名函数的实现

    本文主要介绍了Golang中匿名函数的实现,包括直接调用、赋值给变量及定义全局匿名函数三种方式,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2025-06-06
  • Go语言中的if条件语句使用详解

    Go语言中的if条件语句使用详解

    这篇文章主要介绍了Go语言中的if条件语句的使用,包括if...else语句以及相关嵌套,需要的朋友可以参考下
    2015-10-10
  • 浅谈golang二进制bit位的常用操作

    浅谈golang二进制bit位的常用操作

    这篇文章主要介绍了浅谈golang二进制bit位的常用操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • Go语言基础类型及常量用法示例详解

    Go语言基础类型及常量用法示例详解

    这篇文章主要为大家介绍了Go语言基础类型及常量的用法及示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2021-11-11
  • Goland IDEA项目多开设置方式

    Goland IDEA项目多开设置方式

    这篇文章主要介绍了Goland IDEA项目多开设置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • Go语言中GORM存取数组/自定义类型数据

    Go语言中GORM存取数组/自定义类型数据

    在使用gorm时往往默认的数据类型不满足我们的要求,需要使用一些自定义数据类型作为字段类型,下面这篇文章主要给大家介绍了关于Go语言中GORM存取数组/自定义类型数据的相关资料,需要的朋友可以参考下
    2023-01-01
  • Go语言字符串处理库strings包详解

    Go语言字符串处理库strings包详解

    本文详细介绍了Go语言中的strings库的使用方法,包括字符串的查找、替换、分割、比较、大小写转换等操作,strings库是Go语言中非常重要且功能丰富的标准库,几乎涵盖了所有字符串处理的需求
    2024-09-09

最新评论