不能直接用 map[string]net.conn 存连接,因并发读写会 panic 且 sync.map 的 loadanddelete 不保证回调顺序,易致 write: broken pipe 或 goroutine 泄漏;应改用 map[uint64]connmeta 配 sync.rwmutex,并严格管控连接生命周期与 goroutine 协作。

为什么不能直接用 map[string]*net.Conn 存连接
并发读写 panic 是最直观的后果:fatal error: concurrent map read and map write。但更深层的问题是:sync.Map 的 LoadAndDelete 不保证回调顺序,你在心跳超时 goroutine 里删键、在断连 goroutine 里删键、在重连复用逻辑里又查键——三者竞态下,可能连接已关但键还在,也可能键删了但 conn 还在写,导致 write: broken pipe 或 goroutine 泄漏。
实操建议:
- 用
map[uint64]*ConnMeta配sync.RWMutex,读多写少场景下读锁开销极低 -
ConnMeta必须内嵌sync.Once和atomic.Bool控制关闭状态,避免多个 goroutine 反复调conn.Close() - 删除连接不能在
conn.Read()的 goroutine 里直接delete(m, id),应发信号给统一清理协程(比如通过chan uint64)
如何让每个连接真正“活”够时间
长连接不是靠不关它就自动存活的。NAT、SLB、运营商网关普遍 5–15 分钟空闲回收,net.Conn 自身完全不感知。只设 SetKeepAlive(true) 没用——Linux 默认 keepalive 探测间隔是 2 小时,且多数中间设备根本不转发 TCP keepalive 包。
实操建议:
- 心跳必须应用层发起,且服务端收到后立刻调
conn.SetReadDeadline(time.Now().Add(30 * time.Second));客户端发完也要设对应读超时等回包 - 心跳内容建议带字段,如
{"type":"ping","ts":1720941360,"seq":123},避免被中间设备静默丢弃 -
SetReadDeadline只对下一次Read()生效,每次成功读(包括心跳)后都得重置,否则第二次读就超时返回io.EOF - 写操作同样要配
SetWriteDeadline,超时即退出,防止 goroutine 卡死在阻塞写
goroutine 生命周期怎么跟连接对齐
常见错误是 handler 函数返回就 defer conn.Close(),或者读写混在一个 goroutine 里。前者让连接秒关;后者一卡全卡,还容易 panic:concurrent write to connection。
实操建议:
- 读和写必须分离:一个 goroutine 循环调
conn.ReadMessage(),另一个 goroutine 从send chan []byte拿数据调conn.WriteMessage() - 读 goroutine 退出时,用
context.WithCancel或chan struct{}通知写 goroutine 停止,而不是等它自己发现连接断 - 别在
defer conn.Close()外部启动写 goroutine——Close()不会中断正在阻塞的Write() - 用
sync.WaitGroup跟踪活跃 goroutine 数,服务重启时可等待它们自然退出
模块对外暴露的连接管理接口该怎么设计
暴露原始 *net.Conn 或 *websocket.Conn 给调用方,等于把生命周期控制权交出去。一旦上层忘记 close、没配 deadline、或并发写,你的模块就成背锅侠。
实操建议:
- 对外只暴露封装后的
*Client类型,内嵌连接元信息(ID、创建时间、最后活跃时间)、读写 channel、以及Close()方法 -
Close()方法内部做原子标记、触发清理信号、等待读写 goroutine 退出,而不是直调conn.Close() - 提供
Send(ctx, msg)方法,内部走写 channel 并带超时控制,失败时返回明确错误(如ErrClientClosed、ErrSendTimeout) - 拒绝暴露底层 conn,连
SetReadDeadline都不开放——由模块内部按心跳策略统一管理
真正麻烦的从来不是建连接,而是连接活着的时候你忘了它还在读、还在写、还在等响应。所有清理动作都要有明确归属:谁起的 goroutine,谁负责通知它停;谁存的连接,谁负责删键+关 conn+清资源。错一步,泄漏就藏进日志死角里。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











