
Go 语言中使用 channel 进行跨 goroutine 通信时,若接收端仅执行一次 <-ch 操作,将无法持续接收后续消息;必须通过循环持续读取 channel,否则连接会提前关闭、消息丢失。
go 语言中使用 channel 进行跨 goroutine 通信时,若接收端仅执行一次 `
在您提供的代码中,Websock 函数仅执行了一次 <-messages 接收操作:
func Websock(ws *websocket.Conn) {
msg := <-messages // ⚠️ 只接收一次!
log.Println("only one message arrives, even if I create for two events", msg)
err := websocket.Message.Send(ws, msg)
}这导致函数在收到第一条消息后立即退出,websocket.Conn 随之被关闭(Go 的 http 服务在 handler 返回后自动关闭连接),后续写入 messages channel 的消息无人消费,造成“只能收到一条消息”的现象。
✅ 正确做法是:在 Websock 中启动一个持续接收循环,并配合 defer ws.Close() 确保资源释放:
func Websock(ws *websocket.Conn) {
defer ws.Close() // 确保连接最终关闭
for {
select {
case msg := <-messages:
log.Println("received:", msg)
if err := websocket.Message.Send(ws, msg); err != nil {
log.Printf("send error: %v", err)
return // 连接异常时主动退出循环
}
}
}
}⚠️ 注意事项:
- messages 是无缓冲 channel(make(chan string)),若接收端未及时读取,发送端会在第二次 messages <- ... 时永久阻塞(如您的 ExampleNewWatcher goroutine 中)。虽然当前因 watcher.Events 循环仍在运行,但逻辑上已不可靠。
- 建议为 messages 添加合理缓冲(如 make(chan string, 16)),避免生产者因消费者延迟而卡死。
- ExampleNewWatcher 中 defer watcher.Close() 写法错误(多了一个 0),应为 defer watcher.Close();且 done := make(chan bool) 未被关闭或写入,导致 <-done 永久阻塞 —— 这会使 main 协程挂起,实际 http.ListenAndServe 可能根本未启动。建议移除该阻塞逻辑,改用 sync.WaitGroup 或 context 控制生命周期。
- 若需支持多个客户端同时监听同一事件流,应考虑为每个 WebSocket 连接单独复制消息(如使用 fan-out 模式 + sync.Map 管理 client channels),而非共享单一全局 channel。
总结:channel 是 Go 并发通信的基石,但“一次接收 ≠ 持续监听”。任何需要长期响应消息的 handler(尤其是网络连接场景),都必须显式编写接收循环,并妥善处理错误与连接生命周期。


















