Go通道首次API请求无数据发送问题求助
问题描述
我有一个SSE端点用于向前端发送数据,数据来自另一个API端点。我对Go中的channels还比较陌生,目前发现必须触发两次API端点,SSE端点才会向前端发送一次数据。目前我还未编写用于建立SSE连接的前端代码,仍在调试通道相关逻辑。请问为何需要调用两次API才能让SSE发送数据?
SSE路由
func SendSSE(appCtx *fiber.Ctx, dataChannel chan string) error { appCtx.Set("Content-Type", "text/event-stream") appCtx.Set("Cache-Control", "no-cache") appCtx.Set("Connection", "keep-alive") appCtx.Set("Transfer-Encoding", "chunked") appCtx.Context().SetBodyStreamWriter(fasthttp.StreamWriter(func(w *bufio.Writer) { log.Println("SSE Opened") for { log.Println("Retrieving data channel..") fmt.Fprintf(w, "data: Message: %s\n\n", <-dataChannel) log.Println(<-dataChannel) err := w.Flush() if err != nil { // 浏览器刷新页面会建立新的SSE连接,但只有最后一个连接存活,因此需要在此关闭无效连接 fmt.Printf("Error while flushing: %v. Closing http connection.\n", err) break } } })) fmt.Println("SSE Closed") return nil }
向SSE发送数据的API
func GetApiData(appCtx *fiber.Ctx, dataChannel chan string) error { log.Println("Sending DataChannel data") dataChannel <- "Data is passed from api to sse" return appCtx.Status(http.StatusOK).SendString("Not Implemented") }
Fiber日志

解决方案
问题出在SendSSE函数的循环逻辑中:你每次循环对dataChannel执行了两次读取操作:
- 第一次读取:
fmt.Fprintf(w, "data: Message: %s\n\n", <-dataChannel),获取的数据用于生成SSE响应内容。 - 第二次读取:
log.Println(<-dataChannel),再次读取通道数据仅用于打印日志。
Go的通道(尤其是无缓冲通道)遵循发送-接收一一对应的规则,每次发送操作需要匹配一次接收操作。因此你调用一次API仅完成一次通道发送,而SSE循环一次需要两次接收,必须调用两次API才能完成一次响应写入流程。
修复方式是先将通道数据读取到变量中,再复用该变量:
func SendSSE(appCtx *fiber.Ctx, dataChannel chan string) error { appCtx.Set("Content-Type", "text/event-stream") appCtx.Set("Cache-Control", "no-cache") appCtx.Set("Connection", "keep-alive") appCtx.Set("Transfer-Encoding", "chunked") appCtx.Context().SetBodyStreamWriter(fasthttp.StreamWriter(func(w *bufio.Writer) { log.Println("SSE Opened") for { log.Println("Retrieving data channel..") msg := <-dataChannel // 仅读取一次通道数据 fmt.Fprintf(w, "data: Message: %s\n\n", msg) log.Println(msg) // 复用变量打印日志 err := w.Flush() if err != nil { fmt.Printf("Error while flushing: %v. Closing http connection.\n", err) break } } })) fmt.Println("SSE Closed") return nil }
修改后,每次调用一次API,通道发送的数据会被读取一次,同时用于生成响应和打印日志,无需两次调用API即可正常触发SSE数据推送。
内容的提问来源于stack exchange,提问作者danialsaufi
相关产品推荐
相关产品推荐

