

新闻资讯
技术学院gorilla/websocket需用Upgrader升级HTTP连接,禁用默认跨域限制;读写须单goroutine,加锁管理连接池;需心跳保活、设读写超时、Nginx反向代理配置适配。
gorilla/websocket 实现基础连接与消息收发Go 官方标准库不提供 WebSocket 支持,gorilla/websocket 是最成熟、被广泛采用的第三方实现。它轻量、稳定,且对并发连接和错误处理有良好抽象。
关键点在于:服务端需显式升级 HTTP 连接,不能直接用 http.HandleFunc 返回普通响应;客户端发起的 ws:// 请求必须被 websocket.Upgrader 拦截并转换为长连接。
Upgrader.CheckOrigin 默认拒绝所有跨域请求,开发时需显式允许(如返回 true),上线务必按需校验 r.Header.Get("Origin")
conn.ReadMessage() 和 conn.WriteMessage() 都是非线程安全的,不能在多个 goroutine 中并发调用同一连接conn.SetReadDeadline() 防止客户端假死占用资源;超时后 ReadMessage() 会返回 *net.OpError,需主动关闭连接func handleWebSocket(w http.ResponseWriter, r *http.Request) {
upgrader := websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool { return true },
}
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
http.Error(w, "Upgrade error", http.StatusUpgradeRequired)
return
}
defer conn.Close()
for {
_, msg, err := conn.ReadMessage()
if err != nil {
if !websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
return
}
break
}
if err := conn.WriteMessage(websocket.TextMessage, msg); err != nil {
break
}
}
}
没有内置“广播”机制,必须自己维护连接集合。常见错误是直接用 map[*websocket.Conn]bool 并发读写——这会导致 panic。必须加锁,且锁粒度要细:读写 map 本身需互斥,但对单个 conn.WriteMessage() 的调用应尽量不持锁。
sync.Map 存储活跃连接,键可设为自增 ID 或 session token,避免用 *websocket.Conn 作 key(指针不稳定)conn.WriteMessage() 可能因网络中断立即失败,需捕获 websocket.IsCloseError(err, ...) 和 io.ErrClosedPipe 等错误,并从集合中移除该连接conn.SetWriteDeadline() 后再写——如果某个连接慢,会拖累整个广播。应为每个连接单独设置 deadline 并忽略其超时错误浏览器或移动端网络不稳定,onclose 不一定触发,服务端无法感知断连。单纯依赖 TCP keepalive(默认 2 小时)远远不够。
立即学习“go语言免费学习笔记(深入)”;
websocket.PingMessage(如每 30 秒),服务端用 conn.SetPingHandler() 响应;若超时未收到 ping,视为失联conn.SetPongHandler() 配合定时器,每 25 秒调用一次 conn.WriteMessage(websocket.PingMessage, nil);注意:ping 必须在 write lock 内发送,避免与业务消息竞争gorilla/websocket 本身不处理集群间广播、连接数限制、TLS 卸载、反向代理兼容性等问题。这些不是“可选项”,而是上线前必须补上的环节。
proxy_http_version 1.1、proxy_set_header Upgrade $http_upgrade、proxy_set_header Connection "upgrade"
ulimit -n),通常需调至 65535+;同时 Go 的 GOMAXPROCS 不宜设得过高,避免 goroutine 调度开销压垮 CPU真正难的从来不是“怎么连上”,而是“怎么在千人并发、弱网、滚动发布、节点故障时,依然让消息不丢、不断、不乱序”。这些细节藏在 SetReadDeadline 的时间设定里,藏在 sync.Map 的使用姿势里,也藏在 Nginx 那几行容易被复制粘贴错的配置里。