跳转到主内容
极星编程网:以代码为星,赴技术山海!

Golang如何一键清理十万僵尸空闲长连接

Go的http.Server无一键清理僵尸连接功能,必须通过显式配置ReadTimeout、WriteTimeout、IdleTimeout(最关键,如30秒)和ReadHeaderTimeout来预防连接卡住;已卡死连接只能靠Shutdown优雅终止或重启。 Go 的
http.Server
本身不支持“一键清理僵尸连接” Go 标准库的
http.Server
没有提供类似
CloseIdleConns()
这样的全局清理接口。所谓“十万僵尸空闲长连接”,通常是客户端断开后,服务端 TCP 连接未及时回收(处于
TIME_WAIT
或半关闭状态),或
Keep-Alive
连接被客户端异常丢弃但服务端还在等读——这时连接卡在
read
系统调用上,表现为 goroutine 泄漏 + 文件描述符堆积。 根本原因不是连接“多”,而是连接“卡住没退出”。所以重点不在“清理”,而在“不让它卡住”。
http.Server
必须显式配置超时字段才能控制空闲连接生命周期 默认情况下,
http.Server
的
ReadTimeout
、
WriteTimeout
、
IdleTimeout
全为 0,意味着不限制——这正是十万空闲连接的温床。 关键配置项(必须全部设): 立即学习 “ go语言免费学习笔记(深入) ”;
ReadTimeout
:限制从连接建立到读完请求头/体的总时间(防慢速攻击)
WriteTimeout
:限制写响应的总耗时(防后端阻塞导致连接挂起)
IdleTimeout
:限制连接空闲(无读写)的最大持续时间(这才是治“僵尸”的核心)
ReadHeaderTimeout
:建议也设,防止请求头迟迟不来卡死 accept 后的 goroutine 示例:
srv := &http.Server{ Addr: ":8080", Handler: myHandler, ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, IdleTimeout: 30 * time.Second, // ⚠️ 这个最关键,30 秒无活动就关连接 ReadHeaderTimeout: 2 * time.Second, }
已有卡住的连接无法“批量 kill”,只能靠重启或信号触发优雅关闭 一旦连接已卡在
read
上(比如客户端拔网线但没发 FIN),Go runtime 不会主动中断该系统调用——没有类似 Linux
ss -K
那种强制踢掉 socket 的机制。 可行手段只有两个: 调用
srv.Shutdown(context.WithTimeout(...))
:触发优雅关闭,等待活跃请求结束,并拒绝新连接;但**已卡住的连接仍会等到
IdleTimeout
到期才释放**(不会提前中断) 发送
SIGHUP
或
SIGUSR2
(需自己注册 signal handler)+ 调用
Shutdown
:本质还是走优雅路径,不能秒杀 注意:
srv.Close()
是粗暴关闭,会立即断开所有连接(包括正在处理的),可能导致响应截断或客户端报
connection reset
,不推荐用于线上。 真正压测/运维中要盯住的三个指标 “十万空闲连接”往往是症状,背后是配置缺失或客户端行为异常。上线前必须确认: 用
netstat -an | grep :8080 | grep ESTABLISHED | wc -l
或
ss -tn state established '( dport = :8080 )' | wc -l
看真实 ESTABLISHED 数,别只信
lsof -i :8080 | wc -l
(它包含 TIME_WAIT) 检查
/proc//fd/
下文件描述符数量是否逼近
ulimit -n
,卡住连接会持续占 fd 用
go tool pprof http://localhost:6060/debug/pprof/goroutine?debug=2
看是否有大量 goroutine 停在
runtime.netpoll
或
internal/poll.runtime_pollWait
—— 这就是卡在 read 的证据 超时参数设得再严,如果客户端疯狂建连又不发数据(比如 keep-alive 但永远不发下一个 request),
IdleTimeout
就是唯一防线。别指望事后“一键清理”,得靠事前堵住入口和设好自动熔断点。

相关文章