gorilla/websocket是唯一可行选择,因其封装完整握手、帧编解码和错误分类;必须读写分离、设Read/WriteDeadline、用sync.Map管理连接。

别用 hprose-golang 做 WebSocket 微服务 —— 它不是为高并发实时通信设计的,底层没做读写分离、没设超时、不处理连接清理,上线后大概率出现 concurrent write to connection panic 或 goroutine 泄漏。
为什么 gorilla/websocket 是唯一可行选择
标准库 net/http 不提供帧解析能力,手动实现 Upgrade 握手极易出错:Sec-WebSocket-Accept 必须按 RFC 6455 规则计算(base64(sha1(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))),漏字段或算错值,浏览器就报 WebSocket connection to 'xxx' failed。
gorilla/websocket 封装了完整握手、连接劫持(hijack)、帧编解码和错误分类,是 Go 生态中事实标准,且持续维护。hprose-golang 的 rpc/websocket 模块仅支持简单 RPC 调用,不暴露底层 *websocket.Conn,无法控制读写并发模型。
必须做的三件事:读写分离、deadline、连接管理
每个 *websocket.Conn 必须配两个 goroutine:
立即学习“go语言免费学习笔记(深入)”;
- 一个只调
ReadMessage(),负责接收并分发消息 - 一个只调
WriteMessage(),从chan []byte(建议缓冲大小32)取数据发送 - 禁止在读 goroutine 中直接调
WriteMessage(),尤其含 DB 查询或 HTTP 调用
超时必须显式设置:
-
SetReadDeadline()要在每次ReadMessage()前调,不能只设一次 -
SetWriteDeadline()建议设为time.Now().Add(10 * time.Second) - 不设 deadline → 网络抖动时 goroutine 卡在
[IO wait],内存涨、连接堆积
微服务场景下的连接生命周期管理
WebSocket 连接不是“一连永逸”,需主动管理:
- 客户端断开时,
ReadMessage()返回io.EOF或websocket.CloseMessage,此时应关闭写 goroutine 并从连接池中移除 - 服务端主动踢人,需先发
websocket.CloseMessage,再 close 写 channel,最后调conn.Close() - 用
sync.Map存连接(key 为用户 ID 或 session token),避免锁竞争;不要用全局 slice + mutex - 广播场景下,写 channel 缓冲区满会阻塞,建议加 select + default 非阻塞写,丢弃过载消息
真正难的不是建立连接,而是让成千上万连接稳定活过 30 分钟以上 —— 重点永远在超时控制、错误归因、资源回收,而不是“怎么 upgrade”。


















