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