Iris默认路由不支持SSE,因其ctx.JSON、ctx.Text等方法自动设置Content-Type并立即关闭连接,而SSE要求长连接、持续写入data:格式文本块并显式Flush;需手动设text/event-stream、no-cache、keep-alive头,用StreamWriter或ResponseWriter流控实现。

SSE 路由在 Iris 中必须返回 StreamingResponse,且需手动设置 content-type 为 text/event-stream,否则浏览器不会识别为 SSE 连接。
为什么 Iris 默认路由不支持 SSE?
Iris 的 ctx.JSON、ctx.Text 等响应方法会自动设置 Content-Type 并关闭连接,而 SSE 要求连接长期保持打开、持续写入带换行格式的文本块(如 data: ...\n\n)。Iris 没有内置 SseEmitter 类似 Spring 的封装,必须用底层流控方式实现。
- 直接 return
ctx.JSON(...)→ 连接立即断开,前端EventSource报错Failed to load resource - 忘记设
Cache-Control: no-cache→ 某些代理或浏览器可能缓存首条响应,导致后续事件不触发 - 未调用
ctx.ResponseWriter().Flush()→ 数据滞留在缓冲区,前端收不到实时推送
如何正确注册 SSE 路由(Go + Iris)
核心是使用 ctx.StreamWriter 或直接操作 http.ResponseWriter,并确保每次写入后显式刷新。以下是最简可靠写法:
当代理已经知道网站路由或内容URL,并且在启动前需要有效的sitemap XML、sitemap索引或robots.txt引用时,请使用sitemap。这是一个发布构件技能,而不是爬虫或SEO平台。
app.Get("/sse", func(ctx iris.Context) {
ctx.Header("Content-Type", "text/event-stream")
ctx.Header("Cache-Control", "no-cache")
ctx.Header("Connection", "keep-alive")
ctx.Header("X-Accel-Buffering", "no") // 防 Nginx 缓冲
<pre class="brush:php;toolbar:false;">// 获取原始 ResponseWriter
rw := ctx.ResponseWriter()
flusher, ok := rw.(http.Flusher)
if !ok {
ctx.StatusCode(500)
ctx.WriteString("streaming unsupported")
return
}
// 模拟每秒推送一次时间戳
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
for range ticker.C {
now := time.Now().Format("2006-01-02 15:04:05")
// 注意:SSE 格式要求 data: 开头,结尾双换行
_, _ = rw.Write([]byte("data: " + now + "\n\n"))
flusher.Flush() // 关键:必须刷新,否则前端卡住
}})
-
ctx.Header("X-Accel-Buffering", "no")是给 Nginx 反向代理用的,否则它可能攒满 4KB 才吐数据 - 不要用
ctx.WriteString包裹整个循环体 —— 它内部会自动 Flush 一次,但无法保证每次写入都生效 - 实际项目中应加 context 超时控制和连接中断检测(比如监听
ctx.Request().Context().Done())
如何让多个客户端各自收到不同消息?
Iris 本身无连接管理机制,你需要自己维护一个客户端注册表。常见做法是用 sync.Map 存储每个连接对应的 http.ResponseWriter 和 http.Flusher:
// 全局注册表(注意并发安全)
var clients sync.Map // map[string]*clientConn
<p>type clientConn struct {
rw http.ResponseWriter
flusher http.Flusher
id string
}</p><p>// 在 /sse 路由中注册当前连接
id := uuid.NewString()
clients.Store(id, &clientConn{rw: rw, flusher: flusher, id: id})</p><p>// 推送时遍历发送(示例:广播)
clients.Range(func(key, value interface{}) bool {
c := value.(*clientConn)
<em>, </em> = c.rw.Write([]byte("data: hello from server\n\n"))
c.flusher.Flush()
return true
})
</p>
- 务必在连接关闭时从
sync.Map中清理对应项(可用defer+ctx.Request().Context().Done()监听) - 若用 goroutine 持续推送,要避免对已关闭连接重复写入 —— 会导致
write: broken pipepanic - 别把
http.ResponseWriter存进普通 map,它不是线程安全的;sync.Map是唯一推荐选择
真正难的不是写几行流式响应,而是连接生命周期管理:断连检测、超时清理、广播并发控制、反向代理兼容性 —— 这些细节漏掉一个,SSE 就会在高并发或弱网下静默失败。










