go语言框架无内置并发限流,需手动实现;goroutine轻量但有成本,盲目启动易致资源耗尽;应结合协程池、http超时配置与rate.limiter按需限流。

Go语言框架本身不内置全局并发请求数限制,必须手动实现或依赖中间件/第三方库。盲目用 go 启动协程处理每个请求,极易触发系统级资源耗尽(如文件描述符、内存、数据库连接池打满)。
为什么不能靠 goroutine 数量自动限流
goroutine 轻量 ≠ 无成本:每个 goroutine 至少占用 2KB 栈空间,上万并发时仅栈内存就超 20MB;更关键的是,它不感知下游依赖的承载能力(如 database/sql 连接池默认 MaxOpenConns=0,实际可能无限建连)。
- HTTP handler 中直接
go handle(req)是典型反模式,无法控制总并发数 -
runtime.NumGoroutine()是瞬时快照,不能替代限流逻辑 - 没有上下文取消机制时,超时请求仍会持续占用 goroutine 和连接
用带缓冲 channel 实现简单协程池
这是最轻量、无外部依赖的限流方式,适合中小流量服务。核心是把“执行权”变成可调度的令牌。
type WorkerPool struct {
jobs chan func()
wg sync.WaitGroup
}
<p>func NewWorkerPool(maxWorkers int) *WorkerPool {
p := &WorkerPool{
jobs: make(chan func(), maxWorkers), // 缓冲区大小 = 最大并发数
}
for i := 0; i </p><p>func (p *WorkerPool) Submit(job func()) {
p.jobs </p><p>func (p *WorkerPool) worker() {
for job := range p.jobs {
job()
}
}
</p>
- 缓冲
chan func()容量设为最大并发数,Submit会阻塞直到有空位,无需额外锁 - 务必在 handler 中用
context.WithTimeout包裹实际业务逻辑,防止单个 job 卡死池子 - 不要在
worker()里 recover panic,否则该 goroutine 退出后池子容量永久减少
结合 http.Server 的 ReadTimeout / IdleTimeout 配置
很多人只关注业务层限流,却忽略 HTTP 服务器自身的连接管理漏洞。未设超时会导致连接堆积、FD 耗尽。
-
ReadTimeout控制从读取 request header 开始到读完 body 的总时间,建议设为5s级别 -
IdleTimeout更重要:控制 keep-alive 连接空闲多久后关闭,高并发下不设此值,大量僵尸连接会占满net.Conn -
WriteTimeout必须设,尤其当后端响应慢(如日志写入、模板渲染),避免协程卡在WriteHeader或Write
示例配置:
srv := &http.Server{
Addr: ":8080",
Handler: myHandler,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 30 * time.Second,
}
用 golang.org/x/time/rate 做请求速率限制
当需要按 QPS(而非并发数)限流时,rate.Limiter 是标准解法,支持每秒允许多少请求 + 允许多少突发请求。
- 注意它只管“准入”,不管“执行耗时”,所以必须和协程池/上下文超时配合使用
- 不要对每个请求都新建
rate.NewLimiter,应复用单例或按路由分组实例 - 若用在中间件中,需确保
limiter.Wait(ctx)被调用,否则限流不生效;Wait会阻塞并受 ctx 超时控制
常见误用:
// ❌ 错误:没用 ctx 控制等待,可能永久阻塞
limiter.Allow() // 返回 bool,不阻塞但不保证公平
<p>// ✅ 正确:用 Wait,受 context 控制
if err := limiter.Wait(r.Context()); err != nil {
http.Error(w, "too many requests", http.StatusTooManyRequests)
return
}
</p>
真正难的不是加限流,而是判断在哪一层加、加多严——数据库连接池大小、下游 API 的 SLA、机器 CPU 核心数、甚至 GC 压力都会影响最终阈值。这些数值没法抄,得压测后调。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











