iris 框架不提供 sse 封装因其定位轻量高性能,需手动设置响应头、禁用缓冲、按 data:...\n\n 格式写入并 flush;安全管理需 sync.map 维护连接、监听 ctx.done()、检查 write 错误;前端失败常因跨域、路径、代理配置或未 flush。

Iris 框架原生不提供 SseEmitter 类似 Spring Boot 的封装,但能用标准 HTTP 流式响应 + 正确响应头轻松实现 SSE,关键在手动控制连接生命周期和数据格式。
为什么 Iris 不直接暴露 SSE 封装?
Iris 定位是轻量、高性能的 Go Web 框架,它把流式响应能力交由底层 http.ResponseWriter 和 io.Writer 控制,而非抽象成高层 API。这意味着你得自己处理:
— 连接保持(禁用自动 flush)
— 响应头设置(Content-Type: text/event-stream、Cache-Control: no-cache)
— 数据格式(每条消息以 data: ...\n\n 结尾)
— 客户端断连检测(Go 里靠 responseWriter.Hijack() 或检查 write 是否返回 broken pipe 错误)
用 Context.Stream 实现基础 SSE 推送
Iris 提供了 ctx.Stream() 方法,适合简单定时推送场景(如服务器时钟)。注意它默认会缓冲响应,必须显式禁用:
- 设置
ctx.Response().Header().Set("Content-Type", "text/event-stream") - 设置
ctx.Response().Header().Set("Cache-Control", "no-cache") - 调用
ctx.Response().Flush()立即发送响应头,否则浏览器不会建立连接 - 在循环中用
fmt.Fprintf(ctx.ResponseWriter(), "data: %s\n\n", msg)发送消息,并每次调用ctx.Response().Flush() - 用
time.Sleep()控制推送节奏,避免压垮连接
示例片段:
app.Get("/sse", func(ctx iris.Context) {
ctx.Response().Header().Set("Content-Type", "text/event-stream")
ctx.Response().Header().Set("Cache-Control", "no-cache")
ctx.Response().Flush() // 必须
<pre class="brush:php;toolbar:false;">ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
for range ticker.C {
msg := fmt.Sprintf("data: %s\n\n", time.Now().Format(time.RFC3339))
if _, err := ctx.ResponseWriter().WriteString(msg); err != nil {
return // 客户端已断开
}
ctx.Response().Flush()
}})
如何安全管理多个客户端连接?
Iris 没有内置连接池或广播机制,你需要自己维护一个线程安全的客户端列表(比如 sync.Map 或 sync.RWMutex + slice),并在每个连接的 goroutine 中监听关闭信号:
- 每个 SSE handler 启动独立 goroutine 处理推送逻辑
- 用
ctx.Request().Context().Done()监听请求取消(用户关闭标签页) - 写入前检查
ctx.ResponseWriter().WriteString()是否返回 error,常见如write: broken pipe或use of closed network connection - 避免在 handler 内做耗时同步操作(如数据库查询),应提前准备数据或用 channel 异步投递
典型错误:多个 goroutine 并发写同一个 ResponseWriter,或未及时清理已断开连接导致内存泄漏。
前端 EventSource 连接失败的常见原因
即使后端代码看起来正常,前端仍可能收不到消息,多数因以下几点:
- 路径没配对:前端
new EventSource("/sse")对应后端路由必须完全一致(含 trailing slash) - 跨域未处理:Iris 默认不带 CORS 头,需手动加
ctx.Response().Header().Set("Access-Control-Allow-Origin", "*")(生产环境慎用*) - 响应头缺失或拼写错误:
Content-Type必须是text/event-stream(注意中间无空格),Cache-Control必须是no-cache - 服务端未调用
Flush():浏览器收不到首块数据就不会触发onopen - 反向代理(如 Nginx)截断长连接:需配置
proxy_buffering off、proxy_cache off、proxy_read_timeout 3600
调试建议:用 curl -N http://localhost:8080/sse 直接看原始响应流,确认是否持续输出 data: ...\n\n 格式。
真正难的不是推一条消息,而是当并发连接上千时,如何让每个连接都稳定接收、不漏不卡、不拖垮服务——这取决于你如何设计数据分发模型(channel + select?worker pool?)、错误恢复策略(重连间隔、事件 ID 断点续传),以及是否引入 Redis Pub/Sub 做连接解耦。这些 Iris 不管,但它给你足够的控制权去实现。











