共计 2378 个字符,预计需要花费 6 分钟才能阅读完成。
传统 LLM 交互的痛点
在传统的大语言模型(LLM)交互中,通常采用请求 - 响应模式,即客户端发送请求后,服务器需要等待整个响应生成完毕才返回数据。这种方式存在几个明显缺陷:

- 高延迟:生成完整响应可能需要数秒甚至更长时间,用户需要长时间等待
- 内存压力:服务器需要缓存完整响应数据,对于长文本可能导致内存溢出
- 体验差:用户无法实时看到生成过程,缺乏交互感
流式传输技术对比
SSE (Server-Sent Events)
- 优势:
- 基于 HTTP 协议,无需额外端口
- 自动重连机制
- 简单易实现,浏览器 API 友好
- 劣势:
- 仅支持服务器到客户端的单向通信
- 老版本 IE 不支持
- 默认连接数限制(HTTP/1.1 下 6 个)
WebSocket
- 优势:
- 全双工通信
- 更低的协议开销
- 不受同源限制(可跨子域)
- 劣势:
- 需要额外握手过程
- 需要自己实现心跳和重连
- 服务器配置更复杂
Node.js SSE 实现
// express-sse-server.js
const express = require('express');
const app = express();
// SSE 路由
app.get('/stream', (req, res) => {
// 设置 SSE 必需的响应头
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
// 发送初始化数据
res.write('event: connect\ndata: {\"status\":\"connected\"}\n\n');
// 模拟 LLM 流式生成
const messages = ['Hello', 'World', 'How', 'Are', 'You'];
let index = 0;
const interval = setInterval(() => {if (index < messages.length) {
// 格式必须符合 SSE 规范
res.write(`data: ${JSON.stringify({text: messages[index++]})}\n\n`);
} else {clearInterval(interval);
res.write('event: end\ndata: {\"status\":\"completed\"}\n\n');
res.end();}
}, 500);
// 连接关闭时清理
req.on('close', () => {clearInterval(interval);
console.log('Client disconnected');
});
});
app.listen(3000, () => console.log('SSE server running on port 3000'));
前端 EventSource 实现
// sse-client.js
function connectSSE() {const eventSource = new EventSource('http://localhost:3000/stream');
// 通用消息处理器
eventSource.onmessage = (e) => {
try {const data = JSON.parse(e.data);
console.log('Received:', data.text);
// 更新 UI 逻辑...
} catch (err) {console.error('Parse error:', err);
}
};
// 自定义事件处理器
eventSource.addEventListener('connect', (e) => {console.log('Server connected:', JSON.parse(e.data));
});
eventSource.addEventListener('end', () => {console.log('Stream completed');
eventSource.close();});
// 错误处理(会自动重连)eventSource.onerror = (err) => {console.error('SSE error:', err);
// 可在此添加自定义重试逻辑
if (eventSource.readyState === EventSource.CLOSED) {setTimeout(connectSSE, 5000); // 5 秒后重连
}
};
}
// 启动连接
connectSSE();
性能优化策略
- HTTP/ 2 支持:
- 解决 HTTP/1.1 连接数限制
-
启用服务器推送(Server Push)
-
压缩传输:
- 使用
Accept-Encoding: gzip -
注意 SSE 特有的
Content-Type不能压缩 -
分块编码:
- 设置
Transfer-Encoding: chunked -
合理设置 chunk 大小(通常 4 -8KB)
-
数据格式优化:
- 使用二进制协议(如 MessagePack)
- 减少冗余字段
生产环境问题排查
-
Nginx 配置:
proxy_read_timeout 300s; # 适当延长超时 proxy_buffering off; # 禁用缓冲 -
CORS 处理:
app.use((req, res, next) => {res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); next();}); -
连接泄漏检测:
- 监控服务器连接数
- 实现心跳包机制
- 添加连接超时自动关闭
延伸思考
- 如何在不中断流的情况下实现敏感词过滤?
- 流式传输中如何保证数据完整性(如校验和)?
- 对于需要严格顺序的场景,如何处理网络抖动导致的数据乱序?
- 如何设计优雅的降级方案(当流式不可用时回退到普通请求)?
正文完
