Hotdry.
ai-systems

用 SSE 承载多模型流式补全:连接管理与超时参数实战

面向多模型流式输出,给出 SSE 连接管理与断线续传的工程化参数与监控要点。

在多模型推理架构中,Server-Sent Events(SSE)已成为事实标准的流式传输协议。与 WebSocket 相比,SSE 的单向广播特性与 LLM token-by-token 输出的模式天然契合,部署层面也只需普通 HTTP 即可工作,无需额外的双工通道或协议升级。然而,将 SSE 用于生产级多模型服务时,连接管理、超时控制与断线续传的处理细节往往决定了系统的稳定性与用户体验。本文从工程视角给出这三类参数的推荐配置,并补充监控与回滚策略。

SSE 在多模型流式输出中的角色定位

多模型架构下,同一请求可能触发多个后端模型的串行或并行推理。以对话系统为例,用户输入先经过意图识别模型(轻量级 BERT 类),再根据结果调用不同的领域模型生成最终回复。整个链路中,每个模型的输出都需要实时推送至前端,形成多段 SSE 流。如果每个模型独立维护一条 SSE 连接,前端需要管理多个长连接,复杂度陡增。更好的做法是将各模型的输出汇总到统一的流式网关,由网关以单条 SSE 通道推送给客户端。这么做的好处包括:客户端只需维护一条连接、统一的重试与超时逻辑可以在网关层集中实现、以及便于在网关层插入统一的监控与审计。

但统一网关也带来了新的挑战:不同模型的推理耗时差异巨大,从几十毫秒的分类模型到数分钟的生成模型都可能出现在同一条链路上。如果网关不对单模型的输出做超时控制,某一个慢模型会阻塞整条 SSE 通道,导致前端长时间收不到任何数据,用户体验恶化。因此,必须在网关层面为每个模型设置独立的超时参数,并在模型输出完成后立即发送空行或心跳事件,以保持连接的活跃状态。

连接建立与保持的参数选择

生产环境中,SSE 连接的典型配置涉及以下几个 HTTP 头与框架参数:

服务器端配置方面,需要设置 Content-Type: text/event-streamCache-Control: no-cache 以及 Connection: keep-alive。部分框架(如 FastAPI 的 StreamingResponse)默认会关闭缓冲,开发者需确认显式调用 stream_response 并在每次写入后执行 flush。如果后端使用 Nginx 作为反向代理,需要在 location 块中禁用 proxy_buffering 并调大 proxy_read_timeout,推荐值不低于 300 秒,以适配长文本生成的场景。Nginx 配置示例如下:

location /ai/stream {
    proxy_pass http://backend;
    proxy_buffering off;
    proxy_read_timeout 300s;
    proxy_cache off;
}

客户端侧配置需要根据业务场景选择合适的重试策略。浏览器原生的 EventSource 会自动在连接断开后尝试重新建立连接,默认重试间隔为 1 秒,但在高并发场景下可能导致瞬时流量激增。更好的实践是自行封装 fetch + ReadableStream,在捕获到网络错误后使用指数退避算法(exponential backoff)控制重试频率。首次重试延迟建议设为 1 秒,最大延迟不超过 30 秒,最大重试次数控制在 5 次以内,超过上限后应提示用户手动刷新或重新登录。

超时参数的精细化设置

SSE 超时并非单一数值,而是需要区分以下三类场景:

第一类是请求级超时,即从客户端发起请求到收到首个有效数据的最长等待时间。该参数的核心作用是防止后端服务完全不可用时前端无限期挂起。推荐设置为 10 秒,超过该阈值前端应展示骨架屏或加载动画,并向用户给出明确的错误提示。实现时可以在前端使用 Promise.race 结合定时器,当超时触发时主动 abort 请求并切换到降级接口。

第二类是空闲超时,指在连接保持期间,如果超过指定时间没有发送任何事件,客户端或服务器可以主动关闭连接。设置过短会导致长文本生成过程中频繁断连,设置过长则会让服务器资源被无效长连接占用。建议值为 60 秒,期间服务器应每隔 30 秒发送一次空行或心跳事件(data: \n\n),这样既维持了 TCP 通道的活跃状态,又能让客户端检测到连接是否真的有效。

第三类是单模型推理超时,用于控制在统一流式网关中单个模型的生成时间上限。当某个模型超过阈值时,网关应立即发送错误事件并继续处理后续模型,而不是让整条 SSE 流挂起。推荐阈值为 30 秒,适用于大多数生成式模型;若业务对某些模型有更严格的延迟要求,可在此基础上进一步调低。错误事件的统一格式建议如下:

{
  "type": "error",
  "source": "intent_classifier",
  "message": "Model timeout after 30s",
  "code": "MODEL_TIMEOUT"
}

前端在解析事件流时,根据 source 字段判断是哪个模型出了问题,并决定是否向用户展示部分结果。

断线续传与状态恢复的工程实践

在实际生产中,网络抖动、容器重启、负载均衡器的健康检查都会导致 SSE 连接意外中断。断线续传的核心思路是:在服务端记录已推送的事件偏移量,重连后从断点继续推送未完成的内容。

实现层面,可以在网关维护一个基于请求 ID 的内存缓存或分布式缓存(如 Redis),存储每个请求已发送的 token 数量或事件序号。当客户端使用 Last-Event-ID 头重新连接时,网关从缓存中读取偏移量并跳过已推送的事件。需要注意的是,并非所有模型都支持从任意偏移量恢复 —— 流式生成的模型通常只能从开始重新计算,这种情况下网关应在重连后重新调用模型,但向客户端发送一个 resume 事件说明情况。如果模型支持增量输出(如某些基于缓存的推理引擎),则可以真正实现断点续传。

对于多模型串行链路,推荐的恢复策略是:记录每个模型的完成状态,例如 {"intent": "completed", "reply": "partial"}。重连时前端发送最后一个收到的模型状态,网关根据状态决定是否需要重新执行已完成的模型。仅对未完成的模型进行重新推理,可以显著降低响应延迟。

监控指标与告警阈值

运营层面需要关注以下核心指标:

SSE 连接活跃数是最基础的资源指标,通过后端框架的连接计数或 Nginx 的 ngx_stub_status 模块可以获取。建议在连接数超过预期容量的 80% 时触发告警,防止新请求被拒绝。

事件延迟 P99衡量从模型生成 token 到前端收到事件的总时延。推荐采集点设在网关的输出队列,计算方式为事件离开网关的时间戳减去事件生成的时间戳。P99 延迟超过 2 秒时应排查网络链路或后端推理性能。

重连率反映客户端连接的稳定性。计算方式为一定时间窗口内的重连次数除以总连接次数。正常情况下重连率应低于 5%,如果超过该阈值,可能意味着后端服务存在间歇性不可用或网络质量不佳。

模型超时占比统计各模型超时错误占总事件数的比例。某模型该指标突然上升往往预示着该模型实例或依赖服务出现异常,是容量扩容或故障排查的重要信号。

总结与参数清单

将 SSE 应用于多模型流式推理时,推荐的工程化参数如下:请求级超时 10 秒、空闲超时 60 秒配合 30 秒心跳、单模型推理超时 30 秒、前端重试使用指数退避首次延迟 1 秒最大 30 秒最多 5 次、Nginx 端关闭缓冲并设置 300 秒读取超时。监控方面重点关注连接活跃数、事件延迟 P99、重连率以及各模型的超时占比。网关层实现基于请求 ID 的偏移量缓存,配合前端发送的 Last-Event-ID 实现断线续传。

这些参数并非一成不变,而应结合业务实际的模型复杂度、用户对延迟的敏感度以及基础设施的承载能力进行调优。建议在灰度发布阶段设置较保守的阈值(如请求超时 5 秒),观察线上表现后再逐步放宽至推荐值。

资料来源:本文技术细节参考了 FastAPI 流式响应文档、Nginx 官方代理配置说明,以及业界关于 SSE 与 LLM 流式输出的实践总结。

查看归档