BLOG

SSE 流式协议(Server-Sent Events)

2026/04/25 6 min read BLOG 编程学习之路 SSE 流式协议(SERVER SENT EVENTS)

这个东西跟ai大模型的关系很大


一、先说这套系统在干什么(目的)

你这个 Token Pool / Gateway 系统,本质是在做一件事:

把多个上游 AI key / provider,统一包装成一个 OpenAI API,让客户端随便用,还能自动切换、重试、抗失败。

也就是一个“AI 代理路由器”:

客户端(Codex / OpenAI SDK)

你的 Gateway(key.tengokukk.com)

多个 provider / key pool

真正的 OpenAI / Claude / Responses API

二、你遇到的问题本质是什么(一句话)

不是 AI 坏了,是“请求调度系统 + 重试系统 + 流式协议”三者一起错位了。


三、拆成 4 个核心知识点(你这次全部踩到了)

① SSE 流式协议(Server-Sent Events)

它是干嘛的?

AI streaming 的本质:

不是一次性返回结果
而是一边生成一边推:
 
data: token1
data: token2
data: token3
...
data: DONE

最后必须有:

response.completed

你的问题:

👉 有些请求 最后这个“结束信号丢了”

于是客户端就会:

“我还在等结果,但连接断了?”

表现就是:

  • 卡住

  • 或报:stream closed before response.completed


② 请求生命周期(Request Lifecycle)

一个完整 AI 请求不是一条直线,是:

请求开始

选择 provider

发起 stream

持续输出 tokens

正常结束 / error

必须 emit completion

你这里的问题:

有些路径变成了:

stream 断了
但 completion 没发

👉 客户端以为“被掐线了”


③ Retry 机制(重试系统)

正常设计应该是:

失败 → 换 provider → 再试 → 再失败 → 再换

你这里的问题有两个:

❌ 问题1:没有真正换 provider

日志显示:

retry 1/5 → provider A
retry 2/5 → provider A
retry 3/5 → provider A

👉 等于“假装在重试,其实一直打同一个坏节点”


④ Bug:null 被当成 0(非常关键)

这是你最核心的隐蔽 bug:

原始设计意图:

null = 没配置 → 用默认值

实际代码发生了:

Number(null) === 0

后果:

maxCredentialSwitchRetries = null

被当成 0

= 禁止重试

👉 系统直接“不会切 key 了”


四、为什么症状会“变来变去”

你看到的现象变化,其实是不同层被修复后“漏斗往下掉”:


第一阶段(协议问题)

stream断 + completion丢

👉 SSE 层坏


第二阶段(修完 SSE)

卡几秒 → 消失

👉 变成 retry + routing 问题


第三阶段(修 retry)

开始 retry 但没效果

👉 provider selection bug


第四阶段(发现根因)

null → 0
= retry 被关闭

👉 控制层逻辑错误


五、这些模块分别在系统里干什么

你可以这样理解整个架构:


1️⃣ 协议层(SSE / Responses)

👉 负责“怎么把 AI 输出流给用户”

问题类型:

  • token 不流

  • completion 丢

  • 连接断


2️⃣ 路由层(Provider Selector)

👉 负责“用哪个 AI key”

问题类型:

  • 老是选同一个坏节点

  • 没做隔离

  • retry 不生效


3️⃣ 控制层(Retry / Policy)

👉 负责“失败了要不要换”

问题类型:

  • null 被当 0

  • retry 被关闭

  • 策略丢失


4️⃣ 观测层(Logs / Debug / Health)

👉 负责“你怎么知道发生了什么”

问题类型:

  • 看不到真实 provider 切换

  • retry 不透明

  • 请求链不可追踪


六、为什么会出现这种“工业级复杂 bug”

因为你这个系统其实是:

一个 AI 网关 + 负载均衡 + 代理 + 流式协议 + 多 key 调度器

这类系统天然有 3 个特性:

① 状态很多(stateful)

  • 请求状态

  • provider 状态

  • retry 状态


② 时间敏感(streaming)

  • token 是实时的

  • 不能补发

  • 不能回滚


③ 失败模式复杂

  • 网络断

  • provider 失败

  • quota 失败

  • SSE 断


七、最关键的“本质理解”

如果只记一句话:

这次不是一个 bug,而是 “流式协议 + 路由系统 + 重试策略”三层错位导致的级联失败


八、给你一个更直觉的比喻

你这个系统其实像:

一个外卖调度系统


SSE = 外卖送达通知

(告诉你“送到了/结束了”)

provider = 外卖员

retry = 换骑手

maxCredentialSwitchRetries = 最多能换几次骑手


你的 bug 就是:

  • 外卖员死活送不来(provider坏)

  • 系统还不换人(retry=0)

  • 而且送达通知还丢了(SSE缺 completion)


九、如果你要继续深入(建议)

你这个系统下一步应该补三件“工程级能力”:

1. request trace ID 全链路追踪

👉 每一步都能看到:

req → providerA → fail → providerB → success

2. provider isolation(隔离失败节点)

👉 一个坏节点不能被连续打


3. 强类型 config(避免 null=0)

👉 所有 policy 必须:

undefined = default
null = default
0 = explicitly disable

如果你愿意,我可以帮你把这一套整理成:

“Token Pool / AI Gateway 工业级架构图 + 设计规范(对标 OpenAI / Anthropic)”

会比现在这个 debug 版本更系统化。