Appearance
L01-04 流式输出 SSE
全局中心内容:流式不改变总时长,它改变的是首字节时间和可中断性。 全局讲解主线:同步有多难受 → 流式怎么流出来 → 写出接口 → 证明中断真的省了钱。
P1 · 产出页
中心内容:打字机效果 + 断开连接立刻停止烧钱。
页面内容
GET /api/chat/stream返回 SSE- 浏览器逐字显示
- 客户端断开 → 服务端 cancel
讲解技巧
- 先放最终效果:播 5 秒打字机录屏,一句话不说。视觉冲击比文字描述强十倍。
- 明确说「这一节只要 30 行代码」——降低心理门槛。
时长:20s
P2 · 痛点页:12 秒的空白
中心内容:同步模式的真正损失是钱,不是体验。
页面内容
- 首 Token 0.8s / 完整生成 12s
- 用户以为卡死 → 刷新 → 又烧一次
- 同步下中途放弃,钱照扣
讲解技巧
- 讲一个具体的钱的故事:「用户点了 5 次刷新,每次服务端都完整生成了一遍,5 倍费用,用户什么都没看到」。
- 这页不要讲技术,只讲损失。技术留到 P3。
时长:2min
P3 · 原理图:分片是怎么来的
中心内容:模型吐的是分片,Spring AI 转成 Flux。
页面内容
- 模型 chunk「你」→
Flux<String>→SSE: data: 你 - [DONE] → onComplete → EventSource 关闭
- 模型 chunk「你」→
讲解技巧
- 必须纠正一个误解:「流式不会让模型变快」。总时长几乎一样。讲清楚这点,否则学员后面做性能优化时会找错方向。
- 类比迁移:「就像下载大文件时的进度条——文件不会变小,但你不用干等」。
时长:2min 30s
P4 · 选型表:SSE vs WebSocket
中心内容:95% 的场景 SSE 就够,别上 WebSocket。
页面内容
- SSE:单向、HTTP 穿透代理好、自动重连、极简
- WebSocket:双向、需握手、要自己做心跳重连
- 判据:需要「客户端在生成中途反向控制」才用 WS
讲解技巧
- 主动劝退:明说「大多数声称要 WebSocket 的项目,其实只是要打字机效果」。这种反向建议在课程里很加分。
- 埋下一节课的钩子:真需要打断的场景下一节讲。
时长:2min
P5 · 编码:Controller 三处必须写对
中心内容:produces / .stream() / doFinally,缺一不可。
页面内容
produces = TEXT_EVENT_STREAM_VALUE.stream()而非.call().doFinally(signal -> ...)
讲解技巧
- 先翻车:去掉
produces跑一次,浏览器会一次性等到结束。让学员看到「为什么必须加」。 - 强调
doFinally是唯一能观测中断的钩子——这是后面证明「真的省了钱」的关键。
- 先翻车:去掉
时长:5min
P6 · 编码:需要 usage 时用 chatResponse
中心内容:流式下多数厂商不返回 usage,计费要另想办法。
页面内容
.chatResponse()→Flux<ChatResponse>concatWith补一条 usage 事件- 流式 usage 常缺失 → 估算兜底或事后查
讲解技巧
- 这页是专业度的体现:绝大多数教程不讲流式下拿不到 usage。讲出来学员会觉得「他真的做过生产」。
- 给明确结论:预算用估算,对账用厂商接口或事后统计。
时长:3min
P7 · 前端:beforeunload 必须 close
中心内容:EventSource 会自动重连,不关就是重复烧钱。
页面内容
new EventSource(url)onmessage拼接、onerror关闭window.addEventListener('beforeunload', () => es.close())
讲解技巧
- 先翻车:不写 beforeunload,演示「关掉页面再打开」触发了一次新请求。用日志里的 usage 证明钱被重复花了。
- 这是新手最常踩、也最难自己发现的坑——因为本地测的时候感觉不出来。
时长:4min
P8 · 运行与中断验证
中心内容:日志里看到 signal=cancel 才算真的成功。
页面内容
curl -N看原始 SSE 逐行输出timeout 2 curl -N ...制造中断- 期望日志:
stream finished: signal=cancel
讲解技巧
- 中断验证是本节核心,一定要现场做。看到
cancel那一刻,学员才真正理解「中断生效了」。 - 对比 onComplete 与 cancel 两种 signal,说明它们分别意味着「正常结束」和「被放弃」。
- 中断验证是本节核心,一定要现场做。看到
时长:4min
P9 · 避坑与小结
中心内容:Nginx 会缓冲 SSE,这是上线必踩的坑。
页面内容
proxy_buffering off+proxy_read_timeout 300s- 监控 cancel 占比,异常高说明响应太慢
- HTTP/1.1 下同域名并发连接上限 6 个
讲解技巧
- 第一条要用「内网好好的,一上生产就没了」的口吻讲。这个坑的杀伤力在于它只在生产出现。
- 结尾悬念:「SSE 已经能打字机了,什么场景还需要 WebSocket?」
时长:2min
讲师备忘
| 项 | 内容 |
|---|---|
| 课前必做 | 准备一段 curl -N 的终端录像;准备中断后 signal=cancel 的日志截图 |
| 最容易超时处 | P6 的 usage 缺失,学员会追问计费细节——答「02 篇和 04 篇有完整方案」并收住 |
| 学员最常问 | 「流式比同步慢吗?」答:略慢(多 SSE 帧开销),但首字节快得多 |
| 现场备用 | 网络卡导致打字机不流畅 → 切预录视频,不要现场等 |