Accept后不启goroutine会导致监听循环阻塞,新连接无法接入;必须每次Accept成功后立即go handleConn(conn),且I/O操作、超时设置、连接关闭均需在协程内按规范处理。
Accept后不启goroutine会导致整个监听卡死
现象是连接数一上来,新客户端连不上,netstat -an | grep :端口能看到大量 syn_recv,但服务端日志里 listener.accept() 几乎不打印。这不是负载高,而是主 accept 循环被某个连接的 conn.read() 阻塞住了。
必须在 for { conn, err := listener.Accept() } 循环体内,对每个 conn 立即启动 goroutine:go handleConn(conn)。不要在循环里做任何 I/O 操作——哪怕只是 conn.SetReadDeadline() 也得放进 handleConn 里做。
-
handleConn函数开头别写defer conn.Close(),它可能在写响应中途就被触发,导致后续conn.Write()报write: broken pipe - 如果
handleConn里要查 DB 或调外部 HTTP,必须加context.WithTimeout,否则一个慢请求会拖垮整个连接处理流 - 别用
io.Copy()做中继转发——它每次分配 32KB buffer,万级并发下 GC 会把 CPU 吃满
SetReadDeadline 必须每次读前重置,不能只设一次
很多实现只在 handleConn 开头调一次 conn.SetReadDeadline(time.Now().Add(30 * time.Second)),结果连接空闲 30 秒后就断了。TCP 层没有心跳,云 LB、家用路由器普遍 5–30 分钟静默回收连接,靠“设一次”根本保不住。
正确做法是:每次 conn.Read() 成功返回后,立刻重置 deadline;若 err != nil 且 err.(net.Error).Timeout() 为 true,说明对端已失联,应退出读循环并关闭连接。
- 只用
SetReadDeadline(),别碰SetDeadline()——后者同时影响读写,业务写操作会被误杀 - 收到
"ping"这类心跳包后,必须重置,不能只等业务数据 - 客户端也要对称处理:发完
ping后立刻conn.SetReadDeadline()等pong,超时即重连
读写必须分离到不同 goroutine,且写要串行化
把 Read 和 Write 放同一个 goroutine,看似省事,实则粘包+阻塞双爆。比如两个 conn.Write() 调用被 TCP 缓冲区合并送达,Read() 一次拿到两段消息;更糟的是,写操作慢(如 JSON 序列化)会拖累读超时判断。
每个连接至少起两个 goroutine:go readLoop(conn) 和 go writeLoop(conn)。前者收包、解包、投递到 chan []byte;后者从 channel 取包、封包、发送。
-
writeLoop必须独占写通道,禁止多个 goroutine 并发调conn.Write() - 解包逻辑(如按 4 字节长度头)必须在
readLoop完成,不能丢给上层拼接 - 用
bufio.NewWriter(conn)包一层,buffer 设为32 * 1024,再配合sync.Pool复用实例,避免高频堆分配
禁 Nagle、开 KeepAlive、复用 bufio 实例缺一不可
默认 net.Conn 不禁 Nagle,小包高频发送时会攒包,造成几十毫秒延迟毛刺;也不开 TCP keep-alive,依赖应用层心跳成本高;更不会复用 bufio.Reader/Writer,导致 GC 压力陡增。
这些都得手动补全:在 listener.Accept() 返回的 conn 上立刻做类型断言并设置:
if tcpConn, ok := conn.(*net.TCPConn); ok { tcpConn.SetNoDelay(true) }if tcpConn, ok := conn.(*net.TCPConn); ok { tcpConn.SetKeepAlive(true); tcpConn.SetKeepAlivePeriod(30 * time.Second) }-
reader := bufPool.Get().(*bufio.Reader); reader.Reset(conn),用完bufPool.Put(reader)
最容易被忽略的是:这些设置必须在第一次 Read() 或 Write() 之前完成。Docker + SNAT 场景下,晚设一步,ss -i 就显示 nodelay 仍是 off。

















