Go中可用带缓冲channel实现令牌桶限流:初始化容量为maxTokens的channel并预填满,请求时select尝试取令牌,成功则处理,失败则拒绝;后台goroutine定期补充令牌。

在 Go 中实现请求限流,用 Channel 和计数器是最轻量、最易理解的方式之一。核心思路是:用一个带缓冲的 channel 当作“令牌桶”,每次请求先尝试获取一个令牌(从 channel 取值),成功则处理请求,失败则拒绝;后台 goroutine 定期往 channel 里放令牌(恢复配额)。配合简单计数器还能做更细粒度的窗口限流(比如每秒最多 N 次)。
这是最典型的 channel 限流方式,适合控制平均速率和应对突发流量。
select { case 尝试非阻塞取令牌
示例代码片段:
func NewTokenBucket(maxTokens int, interval time.Duration) *TokenBucket {
ch := make(chan struct{}, maxTokens)
for i := 0; i < maxTokens; i++ {
ch <- struct{}{}
}
tb := &TokenBucket{ch: ch}
go func() {
ticker := time.NewTicker(interval)
defer ticker.Stop()
for range ticker.C {
select {
case ch <- struct{}{}:
default: // 已满,不覆盖
}
}
}()
return tb
}
<p>func (tb *TokenBucket) Allow() bool {
select {
case <-tb.ch:
return true
default:
return false
}
}如果需要严格限制“最近 1 秒内最多 100 次”,单纯 channel 不够精确,这时用 sync/atomic 配合时间戳分片更合适。
立即学习“go语言免费学习笔记(深入)”;
atomic.Int64 存储当前总请求数,同时定期清理过期 slot简易版(固定窗口):
type FixedWindowLimiter struct {
mu sync.RWMutex
count int64
lastReset time.Time
limit int64
window time.Duration
}
<p>func (l *FixedWindowLimiter) Allow() bool {
l.mu.Lock()
defer l.mu.Unlock()</p><pre class="brush:php;toolbar:false;">now := time.Now()
if now.Sub(l.lastReset) >= l.window {
l.count = 0
l.lastReset = now
}
if l.count >= l.limit {
return false
}
l.count++
return true}
真实服务常需双重防护:既不让瞬时并发打爆 CPU(channel 控制 goroutine 数量),也不让单 IP 狂刷接口(计数器按 key 限频)。
MaxConns)注意点:
sync.Map 或加锁expirable.Map 或自己定时扫描select { case
虽然手写能帮你深入理解,但线上建议用经过压测的库:
它们已处理好时钟漂移、高并发竞争、内存回收等细节,自研容易踩坑。
基本上就这些。channel 适合快速控并发,计数器适合按维度控频次,两者不冲突,可以叠加用。关键不是选哪种,而是清楚你要防的是什么——是雪崩?是爬虫?还是资源耗尽?想明白这点,方案自然就清晰了。
以上就是如何在Golang中实现请求限流_使用Channel和计数器控制并发的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号