WebSocket连接建立后立刻断开,是因为http.ResponseWriter被复用导致Upgrade失败;消息广播性能差因同步遍历连接,应为每连接配writePump goroutine;客户端收不到消息常因帧类型不匹配;部署掉线多因未配置心跳及反向代理超时。

WebSocket 连接建立后立刻断开?检查 http.ResponseWriter 是否被复用
Go 标准库的 http.ServeHTTP 要求每个请求只能写一次响应头;一旦调用 WriteHeader 或隐式写入(比如 Write 时 header 未发),再尝试升级 WebSocket 就会报 http: multiple response.WriteHeader calls 或静默关闭连接。
- 确保
Upgrade前没调用过任何Write、WriteHeader、Redirect - 用
gorilla/websocket时,必须用Upgrader.Upgrade(w, r, nil),且w是原始的http.ResponseWriter,不能是中间件包装过的 - 常见坑:日志中间件、JWT 验证中间件里提前写了响应,导致后续
Upgrade失败
消息广播性能崩了?别在主线程里遍历所有 *websocket.Conn
每条消息都同步遍历全部连接并调用 WriteMessage,会阻塞 HTTP handler,且并发写同一个连接不安全。真实场景下,100 个在线用户广播一次就可能卡住几秒。
- 给每个连接配一个独立的
writePumpgoroutine,从专属 channel 读消息 - 广播时只往每个连接的
chan []byte发一份拷贝,不阻塞 sender - 务必设置
SetWriteDeadline,否则网络卡顿时 write goroutine 会永久堆积 - 避免用全局 map 存连接——读写要加锁,锁竞争严重;改用
sync.Map或分片 map
客户端收不到消息?确认 ReadMessage 和 WriteMessage 的帧类型匹配
WebSocket 协议对文本帧(websocket.TextMessage)和二进制帧(websocket.BinaryMessage)严格区分。服务端用 WriteMessage(websocket.BinaryMessage, ...),但前端 ws.onmessage 只能收文本帧,就会静默丢弃。
- 默认用
websocket.TextMessage,数据用 JSON 编码成[]byte再传 - 若前端明确用
ws.binaryType = 'arraybuffer',服务端才可发BinaryMessage - 调试时抓包看 WebSocket frame type:Chrome DevTools → Network → WS → Frames 标签页
- 注意
gorilla/websocket的WriteJSON底层仍是TextMessage,不是语法糖
部署后频繁掉线?别忽略 SetPingHandler 和反向代理超时
Nginx、ALB、Cloudflare 这类代理默认 60 秒无数据就断连;而 Go 的 websocket.Conn 默认不发 ping,连接空闲就直接被中间件 kill,客户端看到的是 1006 异常关闭。
立即学习“go语言免费学习笔记(深入)”;
- 服务端必须设
upgrader.KeepAlive = 30 * time.Second(gorillav1.5.0+)或手动SetPingHandler - Nginx 配置里加
proxy_read_timeout 300、proxy_send_timeout 300,并确保proxy_set_header Upgrade $http_upgrade和proxy_set_header Connection "upgrade"都存在 - 客户端也要实现 pong 响应,否则服务端 ping 超时会主动关连接
事情说清了就结束。真正难的不是连上 WebSocket,而是让上千人同时在线时不丢消息、不卡顿、不被代理砍断——这些细节全藏在连接生命周期管理里。










