WebSocket必须用iris.WebSocket注册路由,不可在普通GET中调用ws.Upgrade;需配置读写超时、正确使用Broadcast、实现Ping/Pong心跳防幽灵用户。

WebSocket连接初始化必须用iris.WebSocket注册路由
直接在GET路由里调用ws.Upgrade会失败,Iris要求WebSocket端点必须通过iris.WebSocket显式注册。否则浏览器发起ws连接时返回404或500,控制台显示net::ERR_CONNECTION_REFUSED或Error during WebSocket handshake。
正确做法是:先定义websocket.Server实例,再用app.WebSocket("/ws", server)挂载——这个/ws路径就是前端new WebSocket("ws://localhost:8080/ws")要连的地址。
-
iris.WebSocket内部自动处理Upgrade头、协议协商和连接生命周期,别自己写ctx.ResponseWriter().WriteHeader(101) - 不能把WebSocket路由和普通HTTP路由混在同一个
Handle里,Iris会跳过WebSocket中间件 - 如果用了子域名或反向代理(如Nginx),需额外配置
Upgrade和Connection头透传
websocket.Conn的读写必须配对使用SetReadDeadline和SetWriteDeadline
不设超时会导致连接卡死:客户端断网后,服务端conn.ReadMessage()一直阻塞,goroutine堆积,内存泄漏。常见现象是聊天消息突然“断连”,但连接状态仍显示在线。
每个websocket.Conn对象都该在OnConnect回调里设置读写超时,且每次读/写前都要重置:
立即学习“go语言免费学习笔记(深入)”;
conn.SetReadDeadline(time.Now().Add(10 * time.Second))
_, msg, err := conn.ReadMessage()
if err != nil {
return // 断开连接
}
conn.SetWriteDeadline(time.Now().Add(5 * time.Second))
conn.WriteMessage(websocket.TextMessage, msg)- 读超时建议5–15秒,写超时略短(3–5秒),避免消息积压
- 不要在
OnMessage里用defer conn.Close()——关闭动作应由业务逻辑触发,比如用户主动下线或心跳超时 - Iris的
websocket.Conn不是线程安全的,同一连接禁止并发调用WriteMessage
广播消息要用server.Broadcast而不是遍历所有Conn
手动遍历server.GetConnections()再逐个WriteMessage,看似直观,实则极易出错:连接可能正在关闭、写缓冲区满、或已超时未清理。典型错误是write tcp: use of closed network connection panic。
server.Broadcast内部做了连接状态检查、写锁保护和错误跳过,适合群聊场景:
server.Broadcast([]byte(`{"type":"msg","from":"user1","text":"hello"}`))- 若需定向推送(如私聊),用
server.GetConnection(id)取单个连接,但务必判空:if conn != nil { conn.WriteMessage(...) } - 广播内容建议预序列化为
[]byte,避免每次广播都重复json.Marshal,尤其消息高频时CPU飙升 - 注意
Broadcast不保证顺序,多个goroutine同时调用可能乱序;严格顺序需加外部锁或用channel串行化
心跳检测必须靠SetPingHandler和SetPongHandler实现
只依赖TCP keepalive不够——它默认2小时才探测,而WebSocket连接常因NAT超时、移动网络切换在几十秒内中断。没心跳的话,服务端长期认为客户端在线,导致“ ghost user ”(幽灵用户)。
Iris的websocket.Server支持原生ping/pong帧处理:
server = websocket.New(websocket.Config{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
})
server.SetPingHandler(func(conn *websocket.Conn) error {
conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
return conn.WriteMessage(websocket.PingMessage, nil)
})
server.SetPongHandler(func(conn *websocket.Conn) error {
conn.SetReadDeadline(time.Now().Add(30 * time.Second))
return nil
})- Ping间隔建议25–30秒,Pong超时设为两倍(60秒),留出网络抖动余量
- 不要在
SetPingHandler里做耗时操作(如DB查询),它运行在读协程中,会阻塞消息接收 - 移动端弱网下,Pong响应可能延迟,此时
SetReadDeadline必须动态延长,否则误判断线
真实部署时,WebSocket的连接复用、消息压缩(websocket.EnableCompression)、以及与JWT鉴权结合的OnConnect校验,才是容易被忽略的复杂点。


















