Go 的 chan 不支持广播,必须用显式分发+同步控制实现;因其点对点特性、无法感知断连、易卡死/panic及并发 map 迭代问题,需借助 sync.RWMutex+map+超时 select 或 gorilla/websocket 的 Hub 模式保障可靠广播。

Go 的 chan 本身不支持广播,硬用会丢消息、卡死或 panic——必须靠显式分发 + 同步控制来实现。
为什么不能直接用 chan string 广播
channel 是点对点机制:一次 send 只能被一个 recv 消费。如果你启动 5 个 goroutine 同时 range 同一个 chan string,结果只有其中一个能收到消息,其余全部阻塞在 range 上,这不是广播,是抢答。
- 无法感知客户端是否已断开,向已关闭的
chan发送会触发panic: send on closed channel - 没有缓冲的接收管道,一个 client 写慢或挂掉,整个广播循环就卡住
- map 遍历时没加锁,上线/下线并发修改会触发
fatal error: concurrent map iteration and map write
用 sync.RWMutex + map + 带超时 select 实现可靠广播
核心是为每个 client 维护专属的 msgCh chan string,服务端用读写锁保护连接映射表,在广播时遍历并带超时发送。
- 注册新连接时,创建带缓冲的
msgCh := make(chan string, 64),避免接收方慢导致广播协程阻塞 - 广播函数内用
mu.RLock()遍历clients,对每个c.msgCh执行select { case c.msgCh <- msg: ... default: ... },跳过写不进的连接 - 客户端读
msgCh的 goroutine 一旦遇到io.EOF或写错误,必须主动调用注销逻辑:删map条目 + 关闭msgCh - 绝对不要用无缓冲
chan当接收管道,否则一个 client 挂掉,整个广播就停摆
WebSocket 场景下优先用 gorilla/websocket 的 Hub 模式
“Hub” 是事实标准结构,它把连接管理、广播解耦得更干净,比手写锁更安全、更易维护。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
立即学习“go语言免费学习笔记(深入)”;
-
hub.broadcast是一个带缓冲的chan []byte,所有上行消息先投递到这里 - 单独一个
hub.run()goroutine 持续消费该 channel,并遍历hub.connections(map[*connection]bool),向每个连接的c.send写入 - 每个
connection自带独立sendchannel,写失败只影响自己,不会拖垮别人 - 注册/注销走
hub.register/hub.unregisterchannel,由 hub.run 统一处理,天然并发安全
别碰 sync.Cond.Broadcast 做消息广播
它不是为“发消息”设计的,而是为“通知条件可能变了”服务的。常见误用是想用它唤醒一堆 goroutine 一起处理新消息——但问题很多:
- 没调用
cond.Wait()的 goroutine 根本收不到,新上线的 client 就是盲区 - 唤醒后必须在 mutex 下重新检查条件,而你广播的是具体数据,不是布尔状态
- 没法传 payload,还得额外配 channel 或全局变量,反而更复杂、更易出竞态
真正容易被忽略的点是:广播不是“发出去就行”,而是“确保每个在线 client 都收到,且不因个别 client 异常而中断整体流程”。这要求你必须显式管理每个连接的生命周期、写超时、资源清理——没有银弹,只有结构化分治。

















