SSE 实战:两个容易被忽略的坑

背景

最近在项目中遇到两个 SSE (Server-Sent Events) 相关的问题,一个是业务逻辑层面的"提前结束",一个是基础设施层面的"连接超时"。记录一下排查和解决过程,供大家参考。

什么是 SSE?

Server-Sent Events (SSE) 是 HTML5 推出的服务端推送技术,允许服务器通过 HTTP 协议向浏览器单向推送数据。相比 WebSocket,SSE 更轻量,适合服务端→客户端的简单实时场景。

SSE 的特点

  • 单向通信:服务器 → 客户端单向推送
  • 基于 HTTP:无需 WebSocket 协议开销
  • 自动重连:浏览器原生支持
  • 实现简单:相比 WebSocket 更适合简单场景

典型场景:实时通知、进度更新、行情推送、日志流。


坑一:SSE 流提前结束

现象

报告生成进度才 33%,前端就收到"完成"通知,实际内容不完整。

排查

检查代码发现,判断任务完成的逻辑是:

// 错误:用外部服务的 duration_ms 字段判断
if data["duration_ms"] != nil {
    isCompleted = true
}

问题在于:外部服务在进度很低时就返回了 duration_ms 字段,导致客户端误判为已完成。

解决

要有自己的完成判断逻辑,别全信第三方:

// 正确:验证 sections 数量
if len(sections) >= expectedSections {
    isCompleted = true
}

关键点:不要依赖外部服务的"完成信号",要有自己的完成标准。


坑二:SSE 连接 30 秒超时

现象

长连接被网关切断,报告生成中断。

排查

默认的 HTTP 客户端有响应头超时限制:

// 默认配置
&http.Client{
    Timeout: 30 * time.Second, // 默认超时
}

解决

SSE 场景下需要禁用所有超时:

sseClient := &http.Client{
    Timeout: 0, // 无超时
    Transport: &http.Transport{
        ResponseHeaderTimeout: 0,
        // 其他超时配置也要关闭
    },
}

关键点:SSE 流要禁用所有超时,靠应用层心跳保活。


总结

SSE 是轻量级的服务端推送方案,但使用时要注意:

  1. 要有自己的完成判断逻辑:别全信第三方服务的"完成信号"
  2. 长连接要处理超时:禁用默认 HTTP 超时,靠应用层保活

希望这篇文章能帮助大家避免踩坑。