
Go 中使用 channel 实现事件通知时,若接收端仅执行一次 <-ch 操作,将只能获取首条消息;需改用循环接收机制,配合 WebSocket 长连接特性,才能持续处理后续消息。
go 中使用 channel 实现事件通知时,若接收端仅执行一次 `
在您的代码中,Websock 函数仅执行了一次 msg := <-messages,这意味着它只从全局通道 messages 中读取一条字符串后即退出函数体——随后 websocket.Conn 被自动关闭,HTTP 处理器生命周期结束。即使 ExampleNewWatcher 后续持续向 messages 发送新消息,也因无活跃接收者而造成消息阻塞(若通道无缓冲)或被丢弃(若带缓冲但已满),从而表现为“只收到第一条”。
✅ 正确做法是:在 Websock 中启动一个持续监听循环,并确保 WebSocket 连接保持打开状态,直到客户端断开或发生错误:
func Websock(ws *websocket.Conn) {
// 启动 goroutine 持续发送消息,避免阻塞主协程
go func() {
defer ws.Close() // 确保连接最终关闭
for {
select {
case msg := <-messages:
if err := websocket.Message.Send(ws, msg); err != nil {
log.Printf("failed to send message: %v", err)
return // 客户端断开或写入失败,退出循环
}
}
}
}()
// 主协程可选:监听客户端是否主动关闭(如需要双向通信)
// 例如:for { _, _, err := ws.ReadMessage(); if err != nil { break } }
// 此处为单向推送场景,故主协程可直接等待子协程完成(或忽略)
select {} // 防止函数立即返回,维持 handler 生命周期
}⚠️ 注意事项:
- 通道类型与缓冲:当前 messages = make(chan string) 是无缓冲通道,发送方会在无接收者就绪时永久阻塞。建议根据并发压力设置合理缓冲容量(如 make(chan string, 10)),避免 watcher 协程卡死。
- 资源泄漏风险:每个 WebSocket 连接都启动独立 goroutine 监听 messages,若未做连接数限制或超时控制,可能引发 goroutine 泄漏。生产环境应结合 context 或连接池管理。
- 并发安全:多个 Websock 实例共享同一 messages 通道,属于广播模式(所有连接均收到相同消息)。如需定向推送,请改用 map[*websocket.Conn]chan string + 逐个分发。
- Watcher 生命周期:ExampleNewWatcher 中 defer watcher.Close() 在函数返回时才调用,但该函数本身是 goroutine,且 done 通道未被关闭,实际不会触发 Close()。应显式管理 watcher 生命周期,或改用 sync.WaitGroup 协调。
总结:通道不是“消息队列服务”,而是 Go 的同步/通信原语;要实现持续消息消费,接收端必须主动循环 select 或 for range(仅适用于已关闭的 channel)。WebSocket 场景下,务必让接收逻辑与连接生命周期对齐——以 goroutine + 循环 + 错误退出为核心范式。


















