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

如何为Gin框架中使用SSE的/stream接口移除全局WriteTimeout

解决Gin框架SSE接口取消全局WriteTimeout限制的方案

针对你的场景,有几种可靠的方法可以单独为/stream接口取消全局设置的WriteTimeout,以下是具体实现:

方法一:在SSE Stream回调中动态取消超时

直接在你的/stream接口的c.Stream回调函数里,添加取消写超时的逻辑,无需修改全局配置:

v2.GET("/stream", HeadersMiddleware(), r.serveHTTP(), func(c *gin.Context) {
    v, ok := c.Get("clientChan")
    if !ok {
        return
    }
    clientChan, ok := v.(ClientChan)
    if !ok {
        return
    }
    c.Stream(func(w io.Writer) bool {
        // 取消写超时:将WriteDeadline设置为零值(无截止时间)
        if deadlineSetter, ok := w.(interface{ SetWriteDeadline(time.Time) error }); ok {
            _ = deadlineSetter.SetWriteDeadline(time.Time{})
        }

        person := Person{Name: "John", Age: 30}
        jsonBytes, err := json.Marshal(person)
        if err != nil {
            log.Printf("marshal error: %v", err)
            return false
        }
        if msg, ok := <-clientChan; ok {
            c.SSEvent("message", jsonBytes)
            log.Printf(msg)
            return true
        }

        log.Printf("client channel closed")
        return false
    })
})

说明

  • 通过断言w为包含SetWriteDeadline方法的接口,直接操作底层连接的写超时设置
  • time.Time{}零值代表取消所有写超时限制,适配SSE长连接的需求
  • 忽略设置错误是因为如果失败,大概率是连接已关闭,此时返回false终止Stream即可

方法二:用中间件针对特定路径统一处理

如果有多个SSE接口需要取消超时,可以创建一个中间件,统一匹配路径并取消超时:

// 定义取消写超时的中间件
func DisableWriteTimeoutForSSE() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 匹配需要取消超时的路径,这里是/stream
        if c.Request.URL.Path == "/stream" {
            if deadlineSetter, ok := c.Writer.(interface{ SetWriteDeadline(time.Time) error }); ok {
                _ = deadlineSetter.SetWriteDeadline(time.Time{})
            }
        }
        c.Next()
    }
}

然后将中间件注册到目标路由组或接口:

// 注册到v2路由组,对组内所有接口生效
v2.Use(DisableWriteTimeoutForSSE())

// 或者仅在/stream接口上使用
v2.GET("/stream", DisableWriteTimeoutForSSE(), HeadersMiddleware(), r.serveHTTP(), func(c *gin.Context) {
    // 原有业务代码
})

方法三:全局不设置WriteTimeout,为非SSE接口添加超时

如果担心依赖内部实现的稳定性,可以反过来操作:全局Server不设置WriteTimeout,然后为非SSE接口单独添加写超时中间件:

1. 修改全局Server配置

a.httpServer = &http.Server{
    Handler:           c.Handler(a.router.Router),
    ReadTimeout:       cfg.HTTP.ReadTimeout,
    IdleTimeout:       cfg.HTTP.IdleTimeout,
    ReadHeaderTimeout: cfg.HTTP.ReadHeaderTimeout,
    // 移除WriteTimeout配置
}

2. 写超时中间件

func SetWriteTimeout(timeout time.Duration) gin.HandlerFunc {
    return func(c *gin.Context) {
        // 仅为非/stream接口设置写超时
        if c.Request.URL.Path != "/stream" {
            if deadlineSetter, ok := c.Writer.(interface{ SetWriteDeadline(time.Time) error }); ok {
                _ = deadlineSetter.SetWriteDeadline(time.Now().Add(timeout))
            }
        }
        c.Next()
    }
}

3. 全局注册中间件

a.router.Use(SetWriteTimeout(cfg.HTTP.WriteTimeout))

这样既保留了普通接口的超时保护,又满足了SSE长连接的无超时需求。

内容的提问来源于stack exchange,提问作者Grandeas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 06:05:34