引言
近年来,基于 Transformer
的大模型推动了 AI
产业的飞速发展,同时带来了新的技术挑战:
- 流式传输 vs 批量返回:大模型生成的长文本若需一次性返回,会显著影响用户体验,实时推送成为必需。
- 语音交互需求:语音助手要求毫秒级响应,而非等待用户完整输入后再返回结果。
- 高并发负载 :大模型
API
需支持数千甚至数万用户同时在线,对通信效率提出更高要求。
这些需求催生了对实时通信技术的关注,而 SSE
(服务器发送事件)和 WebSocket
成为开发者关注的焦点。
什么是 SSE
(服务器发送事件)?
SSE
(Server-Sent Events
,服务器发送事件)是一种基于 HTTP
协议 的技术,允许服务器主动向客户端推送实时数据。它特别适用于需要流式传输的场景。
在 AI
场景中的应用
它的工作方式非常适用于 AI
应用,如:
- 流式返回
AI
生成文本 :如ChatGPT
或腾讯云大模型API
,用户可逐字接收生成内容。 - 实时分析推送 :如情感分析或
NLP
处理结果的持续更新。 - 任务进度监控:如批量推理任务的实时状态反馈。
SSE
的优点
✅ 简单易用 :基于标准 HTTP
,前端通过 EventSource API
即可实现,无需复杂配置。
✅ 自动重连 :浏览器支持断开后自动重连,默认间隔约 2 秒,可通过 retry
字段自定义。
✅ 高效性:相较于轮询更节省资源,但在高并发下需关注连接管理。
✅ 流式传输:支持即时推送部分结果,降低用户等待延迟。
✅ 兼容性强 :适配现有 HTTP
基础设施,支持 CDN
和代理服务器,可结合缓存优化性能。
SSE
的消息格式
-
数据编码 :必须为
UTF-8
编码的文本。 -
HTTP
头信息:Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
-
事件流格式 :每条消息由多行字段组成,格式为
field: value\n
,消息间以\n\n
分隔。常用字段包括:data
:消息内容event
:事件类型id
:消息唯一标识,用于断线重连retry
:重试间隔(毫秒)
示例消息:
makefile
id: 12345
event: update
data: 这是一条更新消息
id: 12346
event: update
data: 这是一条新的更新消息
在这个示例中,服务器发送了两条消息,每条消息包含 id
、event
和 data
字段,每条消息之间用两个换行符分隔。
SSE
实现示例
后端(Go
):
go
package main
import (
"fmt"
"net/http"
"time"
)
func sseHandler(w http.ResponseWriter, r *http.Request) {
// 设置必要的头信息,确保客户端识别为 SSE 流
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
flusher, ok := w.(http.Flusher)
if !ok {
http.Error(w, "Streaming unsupported", http.StatusInternalServerError)
return
}
// 检查客户端是否断开
go func() {
<-r.Context().Done()
fmt.Println("客户端已断开")
}()
// 模拟发送 10 条事件,每秒一条
for i := 0; i < 10; i++ {
select {
case <-r.Context().Done():
return // 客户端断开时退出
default:
fmt.Fprintf(w, "id: %d\nevent: message\ndata: 第 %d 条消息\n\n", i+1, i+1)
flusher.Flush()
time.Sleep(1 * time.Second)
}
}
}
func main() {
http.HandleFunc("/events", sseHandler)
fmt.Println("服务器启动,监听 :8080")
http.ListenAndServe(":8080", nil)
}
前端(JavaScript
):
javascript
const eventSource = new EventSource('http://localhost:8080/events');
eventSource.addEventListener('message', (event) => {
console.log('收到消息:', event.data);
document.getElementById('messages').innerHTML += `<p>${event.data}</p>`;
});
eventSource.onopen = () => console.log('连接已打开');
eventSource.onerror = () => {
console.error('连接中断,将自动重连');
// 默认重试间隔约 2 秒,可由服务器通过 retry 字段调整
};
什么是 WebSocket
?
WebSocket
是一种基于 TCP
的全双工通信协议,支持客户端与服务器间的实时双向数据传输。
在 AI
场景中的应用
WebSocket
适用于需要 双向通信 的 AI
应用,如:
AI
语音助手:客户端发送语音,服务器返回文本或语音响应。- 实时辅助编辑 :如
AI
驱动的代码补全或文档协作。 - 多用户协作 :多人同时编辑
AI
生成内容。
WebSocket
的优点
✅ 双向通信:支持客户端与服务器间的实时交互。
✅ 高效性:持久连接,帧头开销小,传输效率高。
✅ 低延迟 :减少 RTT
,适合语音或高频交互场景。
✅ 多模态支持:可传输文本和二进制数据(如语音、图片)。
WebSocket
的消息格式
帧结构:
FIN
:是否为消息的最后一个帧Opcode
:帧类型(如文本、二进制、关闭)Payload Data
:实际数据
WebSocket
实现示例
后端(Go
):
go
package main
import (
"log"
"net/http"
"time"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool { return true },
}
func wsHandler(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println("升级失败:", err)
return
}
defer conn.Close()
// 简单心跳机制
go func() {
ticker := time.NewTicker(10 * time.Second)
defer ticker.Stop()
for range ticker.C {
if err := conn.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
return
}
}
}()
for {
msgType, msg, err := conn.ReadMessage()
if err != nil {
log.Println("读取失败:", err)
break
}
log.Printf("收到: %s", msg)
if err := conn.WriteMessage(msgType, msg); err != nil {
log.Println("发送失败:", err)
break
}
}
}
func main() {
http.HandleFunc("/ws", wsHandler)
log.Println("服务器启动,监听 :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
前端(JavaScript
):
javascript
const socket = new WebSocket('ws://localhost:8080/ws');
socket.onopen = () => {
console.log('连接已打开');
socket.send('Hello, Server!');
};
socket.onmessage = (event) => {
console.log('收到消息:', event.data);
document.getElementById('messages').innerHTML += `<p>${event.data}</p>`;
};
socket.onclose = () => console.log('连接已关闭');
socket.onerror = (error) => console.error('连接错误:', error);
SSE
与 WebSocket
的详细对比
特性 | SSE |
WebSocket |
---|---|---|
通信方向 | 单向(服务器 → 客户端) | 双向(服务器 ⇄ 客户端) |
协议 | HTTP (text/event-stream ) |
WebSocket 协议 |
数据格式 | 仅文本(UTF-8 ) |
文本 + 二进制 |
重连机制 | 浏览器自动重连(默认 2 秒,可配置) | 需手动实现(如心跳机制) |
使用场景 | AI 文本流式返回、实时通知、日志更新 |
AI 语音助手、聊天应用、协作编辑 |
实现复杂度 | 简单,基于 HTTP |
较复杂,需处理协议升级和连接管理 |
性能表现 | 适合高客户端数、单向推送,但在高并发下需管理连接 | 适合高频双向通信,需优化资源使用 |
安全性 | 配合 HTTPS 和 CORS 即可 |
使用 wss:// ,需额外配置认证(如 token ) |
边界场景分析
- 高并发 :
SSE
在HTTP/2
下通过多路复用支持数万连接,但服务器需处理大量长连接;WebSocket
并发受限于服务器资源(如文件描述符),需配合集群优化。 - 网络不稳定 :
SSE
的自动重连更适合移动端;WebSocket
需心跳机制维持稳定性。
如何选择?
使用 SSE
的场景
- 服务器单向推送实时数据,如
AI
文本流式返回、股票行情更新。 - 希望利用
HTTP/2
优化连接开销。 - 需要简单实现和调试。
使用 WebSocket
的场景
- 需要双向实时交互,如
AI
语音助手或多人协作工具。 - 传输二进制数据或要求极低延迟。
- 客户端与服务器需频繁通信。
结论:
- ✅
AI
文本流式返回(如ChatGPT API
)建议用SSE
:简单高效,天然支持流式传输。 - ✅
AI
语音助手或高交互应用建议用WebSocket
:双向低延迟,灵活性强。
一句话总结:SSE
适合单向 AI
内容推送,WebSocket
适合高交互 AI
应用! 🚀