Go 1.8+Gin应用压测出现FD泄漏但协程数稳定,求排查方向
排查Go 1.8 + Gin服务端FD堆积与延迟上升问题的实用思路
从你描述的现象来看——FD(客户端TCP连接套接字)从9飙升到200,但协程数稳定在14,同时延迟上升——核心矛盾是连接资源没被及时回收或合理复用,而不是协程泄漏。结合Go 1.8和Gin的特性,我整理了几个具体的排查方向和步骤:
一、先搞清楚FD的真实状态:是活跃连接还是待回收连接
首先用lsof -p <你的应用PID>或者netstat -anp | grep <PID>查看这些FD对应的TCP连接状态:
- 如果是大量
ESTABLISHED状态:说明是持续保持的活跃连接,大概率和HTTP长连接(Keep-Alive)配置有关; - 如果是大量
TIME_WAIT状态:说明连接已经关闭,但系统还在等待2MSL时长回收,可能是短连接场景下的正常现象,但数量过多会占用FD。
二、排查HTTP服务端的超时与长连接配置
Gin基于Go标准库的net/http,而Go 1.8新增了IdleTimeout参数,但默认情况下ReadTimeout、WriteTimeout、IdleTimeout都没有设置,这会导致:
- 空闲的Keep-Alive连接会被无限期保留,压测时客户端持续建立长连接,FD自然会涨;
- 如果客户端连接后不发送请求,服务端会一直持有该FD,直到客户端主动关闭。
你可以尝试添加以下配置到你的Gin服务中:
srv := &http.Server{ Addr: ":8080", Handler: r, // 你的Gin路由器 ReadTimeout: 10 * time.Second, // 读取请求的超时 WriteTimeout: 10 * time.Second, // 响应写入的超时 IdleTimeout: 30 * time.Second, // 空闲长连接的超时时间 }
压测后观察FD是否还持续增长,延迟是否有改善。
另外,也可以临时禁用Keep-Alive测试:srv.DisableKeepAlives = true,如果FD不再增长,说明问题确实出在长连接的管理上。
三、检查请求处理逻辑中的阻塞点
协程数稳定在14,但延迟上升,说明现有协程都被阻塞住了,新请求只能排队等待。这时候连接(FD)会被一直持有,直到请求处理完成或超时。
排查方法:
- 开启Go的pprof工具,在代码中添加:
然后启动服务后访问import _ "net/http/pprof"http://localhost:6060/debug/pprof/goroutine?debug=2,查看每个协程的调用栈,找出哪些操作在阻塞(比如数据库查询无超时、调用外部API未设置超时、同步锁等待等)。 - 检查所有IO操作的超时设置:比如数据库连接的
SetConnMaxLifetime、HTTP客户端的Timeout,确保没有无限期阻塞的情况。
四、考虑Go 1.8版本的潜在问题
Go 1.8是比较老旧的版本(2017年发布),它的net/http包存在一些已修复的网络相关bug,比如某些场景下Keep-Alive连接的空闲超时处理异常,导致FD无法被回收。如果前面的排查都没有结果,可以尝试升级到较新的Go版本(比如Go 1.16+),看问题是否消失。
五、系统层面的小检查
- 确认系统的文件描述符限制:执行
ulimit -n,默认一般是1024,200远低于这个值,所以大概率不是限制问题,但如果后续FD继续增长到接近限制,需要调整; - 查看TCP系统参数:比如
net.ipv4.tcp_tw_reuse,如果是大量TIME_WAIT连接,可以开启该参数让系统复用TIME_WAIT状态的连接,不过这是最后一步的系统优化。
内容的提问来源于stack exchange,提问作者Rudziankoŭ
相关产品推荐
相关产品推荐

