Fluxio
← 返回博客

实时 token 流的断线重连与半截消息处理实测

SSE 与接口ZH

§1 实测场景:把断线、乱序和半截事件放进同一轮测试

这次现场测试的目标很直接,就是在真实网络抖动下观察实时 token 流的恢复行为。我们把客户端接到 Fluxio 的流式接口上,接口基址是 https://api.fluxioapi.com,使用 OpenAI 风格客户端时在后面追加 /v1,然后用 Claude Sonnet 5.5 和 Gemini 3.8 Flash 跑同样的长回答。两条链路都是逐 token 推送,方便观察单个事件被切断的样子。

测试中我们人为制造了三类故障,第一类是在流中途直接切断连接,第二类是把网络延迟拉高到几百毫秒并偶尔丢包,第三类是让客户端在收到半个事件时就退出。每一轮都记录服务端已推送的序号、客户端已确认的序号,以及重连后收到的第一帧内容。

之所以选 Fluxio 做这个实验,是因为它是流式优先、按 token 计费的 AI 接口中继,一个密钥可以调到 Claude、GPT、Gemini、DeepSeek、Kimi、GLM、Qwen、Grok 等模型,回复逐 token 到达,也兼容 Claude Code、Codex、Gemini CLI 和任意 OpenAI SDK,这样断线测试不需要为不同模型各写一套解析代码。

§2 断线重连:从游标续传到整段重放的两条路径

断线发生时,客户端其实只有两个选择,要么带着上次确认的序号请求续传,要么从头重新发起一次请求。我们先试了游标续传,客户端在请求头里带上最后收到的事件序号,服务端从下一个事件继续推送。这种方式在毫秒级断线时表现最好,用户几乎感觉不到停顿。

但游标续传依赖服务端保留一段缓冲,缓冲窗口越短,长断线就越容易丢数据。我们在连接被切断三十秒之后再重连,发现服务端已经把旧缓冲丢弃,续传请求被拒绝,只能退化成整段重放。整段重放虽然可靠,却会让已经渲染到界面上的 token 重复出现一次。

  • – 短断线优先使用游标续传,并把缓冲窗口与业务超时时间对齐
  • – 长断线自动降级为整段重放,同时在客户端保留已渲染文本的指纹
  • – 重连请求必须携带幂等键,避免服务端把同一次回答当成两次生成
  • – 退避时间做成指数增长并加上随机抖动,防止大量客户端同时重连

我们还试了在重连时换模型,比如把补全部分切到 Claude Haiku 4.5 来快速续上剩余内容。这个做法只适合摘要类输出,不适合代码生成,因为新模型的措辞风格会突变,用户能明显看出前后不一致。

§3 半截消息:被切断的流式事件该怎样拼接

逐 token 推送最常见的问题不是断线,而是事件被截断。我们观察到的典型情况是缓冲区里留下一段不完整的 JSON,既不能解析,也不能直接丢弃,否则用户会看到缺字的回答。正确做法是把网络层读到的字节先按事件分隔符切开,完整的事件立刻解析,末尾那段残缺数据留在缓冲区,等下一帧到达后拼上再解析。

这里有两个容易踩坑的细节。第一,不能用文本长度去判断事件是否完整,因为中文在多字节编码里被切开的概率更高,必须依赖流式事件的边界标记。第二,重连之后的第一帧很可能就是半个事件,如果直接当完整事件处理,解析会抛错并把整条流的后续内容一起丢掉。

我们的实现把解码器做成有状态的,断线时保存缓冲区里的残缺字节,重连后先把这段字节和新数据合并,再走正常的切分流程。用 Claude Sonnet 5.5 跑八千 token 长回答时,这个方案把丢字率从百分之零点八压到接近零,而对延迟的影响只有几毫秒。

§4 实时聊天里的顺序、重复与迟到事件

真实聊天界面还要处理另外三种异常,事件重复到达、事件乱序到达、以及迟到的旧连接仍在推送。我们在测试里让旧连接在断开前多跑了几秒,结果发现它和新连接的输出混在一起,界面上出现了两段互相矛盾的回答。

解决办法是给每次生成分配一个请求标识,再给每个事件分配递增序号。客户端只接受当前请求标识的事件,序号小于等于已确认序号的直接丢弃,序号跳号则触发一次续传而不是继续等待。这样即使网络层把同一批数据送了两遍,界面也只会渲染一次。

  • – 用请求标识隔离不同轮次的流,旧连接的事件直接丢弃
  • – 用递增序号做去重和断点判断,跳号时立刻发起续传
  • – 把渲染与落库解耦,落库写入带上序号以保证可重放
  • – 在客户端维护一个最小提交点,未到达的内容宁可留白也不猜测

§5 现场判定:哪些恢复策略值得固化进生产

综合四轮测试,我们的判定是,游标续传加有状态解码器,是实时 token 流最值得固化的组合,它同时解决了断线恢复和半截消息两个问题,而且对用户几乎无感。整段重放保留为兜底手段,只在缓冲过期时启用,并配合指纹去重。

关于重连时的模型切换,我们的结论是不建议在长回答中途换模型,短回答或摘要可以例外。另外,客户端一定要把退避、去重和续传写进同一套状态机,否则这三种逻辑会互相覆盖,出现偶发的重复渲染。

如果你正在为实时聊天或代码补全接入流式接口,可以直接用 Fluxio 复现这套测试,它的逐 token 推送和 OpenAI 风格兼容让你可以在 Claude Code、Codex、Gemini CLI 与任意 OpenAI SDK 之间快速切换模型,会员邀请好友还能拿到好友付费金额 17% 的分成。

欢迎立即注册 Fluxio,开始你的实时流测试。

创建 Fluxio 账号 →

顺流再读几篇