echo.websocket默认不处理断线重连,因其仅封装gorilla/websocket握手逻辑,读写、心跳、重连均由开发者自行实现;服务端不会自动重建连接或缓存消息,需手动集成连接保活、指数退避重连及会话恢复机制。

为什么 echo.WebSocket 默认不处理断线重连
Echo 框架本身不提供 WebSocket 断线重连机制,echo.WebSocket 只是封装了 gorilla/websocket 的握手和连接建立逻辑,后续的读写、心跳、重连全部交由开发者控制。这意味着:客户端断开后,服务端不会自动重建连接,也不会缓存未送达消息;服务端重启或网络抖动时,已建立的连接会静默失效,客户端若不主动重连,就彻底失联。
实际项目中,必须自己实现「连接保活 + 客户端重连策略 + 服务端会话恢复」三者配合,否则用户刷新页面或切后台再回来,就会丢失上下文。
客户端重连不能只靠 setInterval 轮询
常见错误是用定时器不断尝试 new WebSocket(url),但这样既浪费资源,又无法区分「暂时不可达」和「永久失效」。正确做法是结合连接状态 + 指数退避 + 错误码判断:
-
onclose事件中检查event.code:1006(异常关闭)、1001(服务端下线)、4000+(自定义业务码)需重连;1000(正常关闭)则不重连 - 首次失败后等待 1s,第二次 2s,第三次 4s……上限建议 30s,避免雪崩
- 每次重连前生成新连接 ID 并带入 URL 查询参数(如
?cid=abc123),方便服务端识别新会话 - 重连期间暂停发送业务消息,用本地队列暂存(
localStorage或内存数组),连接恢复后再send()
服务端如何识别并恢复用户会话
Echo 里没有内置会话管理,必须自己绑定 WebSocket 连接与用户身份。关键点在于:不要依赖连接对象生命周期,而要用外部存储做状态映射。
推荐做法:
- 客户端首次连接时,携带 token 或 session_id(通过 URL query 或 handshake header 传入)
- 服务端用
jwt.Parse或查 DB 验证后,将connection和用户 ID 存入 Redis,key 为ws:uid:{uid},设置过期时间(如 5 分钟) - 当新连接到达且 uid 已存在旧连接时,主动调用旧连接的
conn.Close(4000, "replaced"),避免双写冲突 - 消息广播时,从 Redis 查 uid 对应的活跃 conn 列表,跳过已关闭连接(用
conn.WriteMessage返回 error 判断)
注意:echo.Context 在 WebSocket 升级后就失效了,所有状态必须显式保存到外部变量或存储中。
echo.WebSocket 中心跳必须手动实现
Echo 不自动发 ping/pong,gorilla/websocket 虽支持 SetPingHandler,但 Echo 的 echo.WebSocket 封装里没暴露该接口。所以必须在升级后的连接上手动设置:
conn, err := upgrader.Upgrade(c.Response(), c.Request(), nil)
if err != nil {
return
}
// 启用心跳:服务端每 30s 发 ping,超时 60s 关闭连接
conn.SetPingHandler(func(appData string) error {
return conn.WriteMessage(websocket.PongMessage, nil)
})
conn.SetPongHandler(func(appData string) error {
conn.SetReadDeadline(time.Now().Add(60 * time.Second))
return nil
})
<p>// 单独 goroutine 定期 ping
go func() {
ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop()
for range ticker.C {
if err := conn.WriteMessage(websocket.PingMessage, nil); err != nil {
break
}
}
}()
</p>
漏掉 SetReadDeadline 或没启动 ping goroutine,会导致连接长期假死——客户端已断,服务端还维持着 conn 对象,内存泄漏+消息堆积。
真正难的不是写重连代码,而是界定「哪些状态该由客户端恢复、哪些必须服务端兜底」。比如聊天消息的已读回执,如果服务端没持久化,仅靠客户端重连时拉取 last_seq,就可能漏掉中间几条。这类逻辑边界,比技术实现更需要提前设计清楚。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











