net.Conn断开后不可复用,自动重连必须由业务层显式实现:需手动管理连接生命周期、采用指数退避重试、正确清理资源并重设读写超时。

net.Conn 一旦断开就不可复用,所谓“自动重连”必须由业务代码显式控制——没有 magic,只有你写的循环、超时和错误分类。
用 net.Listener + conn.Close() 主动模拟断连
测试重连逻辑前,得先让连接真断掉。别依赖网络抖动或杀进程,那样不可控、难复现。
- 起一个本地
net.Listener(比如127.0.0.1:8080),在Accept()后立刻调用conn.Close(),客户端就会收到io.EOF或read: connection reset by peer - 更精细的控制:接受连接后,用
time.AfterFunc(500 * time.Millisecond, conn.Close)模拟“运行中意外断开” - 避免用
os.Process.Kill()杀服务端进程——它可能触发syscall.ECONNREFUSED(连接拒绝),这不是断连,是根本连不上,测不到重连路径
net.Dial 失败场景要分三类构造
重连逻辑是否健壮,取决于你是否覆盖了这三种典型失败:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
-
dial tcp 127.0.0.1:9999: connect: connection refused:目标端口没监听,对应net.OpError,err.(*net.OpError).Err是syscall.ECONNREFUSED,属于可重试的临时错误 -
dial tcp: lookup nonexistent.example.com: no such host:DNS 解析失败,err.(*url.Error).Err是*net.DNSError,IsTemporary()返回true,应重试 -
context deadline exceeded:拨号超时,比如防火墙丢包或 DNS 响应慢,必须重试,且下次需加大net.Dialer.Timeout
验证重连是否真生效,盯住三件事
光看日志打印 “reconnecting…” 不算数,得确认资源、状态、行为都对:
立即学习“go语言免费学习笔记(深入)”;
- 每次重连前,旧
conn必须已调用conn.Close(),否则文件描述符泄漏——用lsof -p $(pidof yourprogram)查看句柄数是否持续上涨 - 重连间隔是否按指数退避执行?比如第 1 次失败后 sleep 1s,第 2 次 sleep 2s,第 3 次 sleep 4s……可用
time.Now()打点比对 - 重连成功后,是否重新设置了
SetReadDeadline和SetWriteDeadline?漏设会导致后续Read()永久阻塞
最易被忽略的点:重连循环里如果用了 defer conn.Close(),那第一次连接失败时 conn 是 nil,defer 会 panic;而连接成功后 defer 又会在函数退出时关掉连接,导致你本想长期持有的连接被提前释放。重连逻辑里,Close() 必须显式、成对、有判空。

















