You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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日志

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 16:01:38