0

0

如何使用Golang实现并发WebSocket通信_Golang多客户端消息推送实践

P粉602998670

P粉602998670

发布时间:2026-01-04 20:35:55

|

760人浏览过

|

来源于php中文网

原创

核心是用注册/注销通道+单hub goroutine统一管理连接,配readPump/writePump双协程分离读写,加心跳检测与错误清理机制,确保1000连接24小时稳定。

如何使用golang实现并发websocket通信_golang多客户端消息推送实践

用 Golang 实现并发 WebSocket 多客户端消息推送,核心不是“能不能”,而是“怎么组织 goroutine 和 channel 才不丢消息、不 panic、不泄漏连接”。gorilla/websocket 本身不管理连接生命周期,所有并发安全、广播逻辑、异常清理都得你亲手搭。

为什么不能直接用 map[*websocket.Conn]bool 存客户端?

并发读写原生 map 会触发 fatal error: concurrent map read and map write。哪怕只是遍历广播时另一个 goroutine 正在删连接,也会崩。

  • 必须用 sync.RWMutex 包裹读写操作,或改用 sync.Map(但注意:它不支持遍历,广播时仍需额外维护 slice 或 channel)
  • 更推荐方案:用注册/注销通道 + 单独的 hub goroutine 统一管理,所有增删都在一个 goroutine 内完成,天然线程安全
  • 示例中常见错误:在 conn.ReadMessage() 的 for 循环里直接 delete(clients, conn) —— 这是并发写 map 的高危操作

conn.ReadMessage()conn.WriteMessage() 必须分离到不同 goroutine

WebSocket 连接是全双工,但阻塞式读写会互相拖慢。比如客户端发了一大段消息卡住读协程,就无法响应 pong 或发送广播,最终触发超时断连。

  • 每个连接至少启动两个 goroutine:readPump(只读、转发消息到 broadcast channel)和 writePump(只写、监听该 client 的 send channel)
  • writePump 中务必用 select + default 或带超时的 send,防止 client 不读消息导致 send channel 堵死,进而卡住整个 hub
  • 别在 writePump 里调 conn.SetWriteDeadline() 后无脑 WriteMessage() —— 若网络抖动,会 panic;应捕获 websocket.IsCloseError(err)io.EOF 并主动退出

心跳检测不是可选项,而是连接存活的唯一可信依据

HTTP 升级后的 WebSocket 连接,底层 TCP 可能静默断开(如 NAT 超时、中间代理 kill 空闲连接),而 Go 代码完全感知不到,直到下次 WriteMessage() 才报错 —— 此时已积压多条消息。

Explainpaper
Explainpaper

阅读学术论文的更好方法,你的学术论文阅读助手。

下载

立即学习go语言免费学习笔记(深入)”;

  • 必须启用 conn.SetPingHandler(),并在 writePump 中用 time.Ticker 定期发 websocket.PingMessage
  • 服务端收到 ping 后自动回 pong,但你要在 readPump 中设置 conn.SetReadDeadline(),超时未收消息即视为离线
  • 别依赖前端发心跳 —— 客户端页面崩溃、标签页休眠、手机锁屏都会中断 JS 执行;心跳必须由服务端主动发起并监控响应

广播消息前,一定要检查 conn.WriteMessage() 返回值

看似简单的 for range clients { conn.WriteMessage(...) },实际极易因某个 client 连接已断却未及时清理,导致 panic 或阻塞。

  • 每次 WriteMessage() 后必须判断 err,对 websocket.IsUnexpectedCloseError(err)io.ErrClosednet.ErrClosed 等做清理:关闭 send channel、从管理器移除、conn.Close()
  • 避免在广播循环中直接 delete map —— 应发信号给 hub goroutine 异步处理,否则可能和注册逻辑冲突
  • 如果用 sync.Map,记得用 LoadAndDelete() 或先 Load() 再显式 Delete(),不要边遍历边删

真正难的从来不是写出能跑通的 demo,而是让 1000 个连接同时在线 24 小时不泄漏内存、不堆积 goroutine、不误杀活跃连接。每一条连接的注册、心跳、读写、清理路径,都得有明确的归属 goroutine 和退出机制 —— 没有银弹,只有层层设防。

相关专题

更多
golang如何定义变量
golang如何定义变量

golang定义变量的方法:1、声明变量并赋予初始值“var age int =值”;2、声明变量但不赋初始值“var age int”;3、使用短变量声明“age :=值”等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

177

2024.02.23

golang有哪些数据转换方法
golang有哪些数据转换方法

golang数据转换方法:1、类型转换操作符;2、类型断言;3、字符串和数字之间的转换;4、JSON序列化和反序列化;5、使用标准库进行数据转换;6、使用第三方库进行数据转换;7、自定义数据转换函数。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

225

2024.02.23

golang常用库有哪些
golang常用库有哪些

golang常用库有:1、标准库;2、字符串处理库;3、网络库;4、加密库;5、压缩库;6、xml和json解析库;7、日期和时间库;8、数据库操作库;9、文件操作库;10、图像处理库。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

336

2024.02.23

golang和python的区别是什么
golang和python的区别是什么

golang和python的区别是:1、golang是一种编译型语言,而python是一种解释型语言;2、golang天生支持并发编程,而python对并发与并行的支持相对较弱等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

207

2024.03.05

golang是免费的吗
golang是免费的吗

golang是免费的。golang是google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的开源编程语言,采用bsd开源协议。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

388

2024.05.21

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

194

2025.06.09

golang相关判断方法
golang相关判断方法

本专题整合了golang相关判断方法,想了解更详细的相关内容,请阅读下面的文章。

189

2025.06.10

golang数组使用方法
golang数组使用方法

本专题整合了golang数组用法,想了解更多的相关内容,请阅读专题下面的文章。

191

2025.06.17

Python /与// 教程大全
Python /与// 教程大全

本专题整合了python的/和//的相关内容大全,阅读下面的文章了解更多详细内容。

0

2026.01.06

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
WEB前端教程【HTML5+CSS3+JS】
WEB前端教程【HTML5+CSS3+JS】

共101课时 | 8.1万人学习

JS进阶与BootStrap学习
JS进阶与BootStrap学习

共39课时 | 3.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号