概述
在现代 Web 应用中,实时数据推送已成为提升用户体验的关键需求。从新闻网站的实时更新、金融行情的动态展示,到 AI 大模型逐字输出的“打字机”效果,用户期望在不主动刷新页面的情况下即时获取最新数据。
然而,传统的 HTTP 协议基于请求-响应模式,客户端必须主动发起请求才能获取新数据。这不仅导致高延迟,还造成频繁轮询带来的资源浪费。Server-Sent Events(SSE,服务器发送事件)作为 HTML5 引入的一种轻量级、基于 HTTP 的单向通信技术,成功地解决了这一痛点。
一、SSE 基础概念
1.1 什么是 SSE?
SSE(Server-Sent Events)是一种基于 HTTP 协议的技术,用于实现服务器向客户端的单向实时数据推送。它也被称为“事件流”(Event Stream),允许服务器在客户端请求保持打开的状态下持续推送更新。
SSE 是 HTML5 规范的一部分,主要由两部分组成:服务端与浏览器的通讯协议(基于 HTTP),以及浏览器端可供 JavaScript 使用的 EventSource 对象。
1.2 SSE 的核心特性
SSE 具备以下几个核心特点:
| 特性 | 说明 |
|---|---|
| 单向通信 | 数据只能从服务器流向客户端,客户端无法通过 SSE 连接向服务器发送数据 |
| 基于 HTTP 长连接 | 通过 HTTP 协议建立连接后,服务器保持连接持久化,持续推送数据 |
| 轻量级协议 | 使用 text/event-stream 文本格式传输,协议开销远低于 WebSocket |
| 自动重连机制 | 浏览器内置断线重连逻辑,可通过 retry 字段控制重连间隔 |
| 事件驱动模型 | 支持自定义事件类型,客户端可针对不同事件分别处理 |
1.3 SSE vs WebSocket vs 传统轮询
理解 SSE 与其他通信方式的差异,是做好技术选型的基础:
| 对比维度 | SSE | WebSocket | 传统 HTTP轮询 |
|---|---|---|---|
| 通信方向 | 服务器 → 客户端(单向) | 服务器 ↔ 客户端(双向) | 客户端 → 服务器(被动请求) |
| 协议基础 | 基于 HTTP | 基于 HTTP 升级(WS/WSS) | 基于 HTTP |
| 连接方式 | 长连接 | 长连接(TCP) | 短连接(每次新建) |
| 数据格式 | 文本格式(事件流) | 二进制或文本帧 | 完整 HTTP 响应体 |
| 实时性 | 高 | 极高 | 低(依赖轮询间隔) |
| 协议开销 | 低 | 中 | 高 |
| 自动重连 | 原生支持 | 需手动实现 | 不适用 |
选型建议:如果业务场景只需要服务器向客户端推送数据(如通知、行情、AI 流式输出),SSE 是更简单、更轻量的选择;如果需要频繁的双向交互(如聊天、在线游戏),则应选择 WebSocket。
二、SSE 适用场景
SSE 最适合服务器需要主动向客户端推送数据的场景:
2.1 AI 大模型流式响应
大模型推理耗时久、输出长,SSE 可以实现“边推理边输出”的流式效果。ChatGPT 等 AI 应用的逐字输出,正是基于 SSE 技术实现。
2.2 实时通知系统
社交平台的好友消息、新动态推送,电商平台的新订单提醒,审核状态更新等。
2.3 实时数据看板
股票行情、汇率变动、设备监控数据、实时日志流等需要高频更新的数据展示场景。
2.4 赛事与新闻直播
体育比赛比分更新、赛事进程推送、突发新闻实时推送。
2.5 文件下载进度
大文件下载或生成过程中的进度实时反馈。
SSE 适用场景的核心特征是:数据更新频繁、对实时性有要求、仅需服务器单向推送。
三、SSE 工作原理
3.1 通信流程
SSE 的工作流程可以概括为以下五个步骤:
- 客户端发起连接:前端通过 EventSource 对象发起 HTTP GET 请求,请求头携带 Accept: text/event-stream 标识流式需求。
- 服务端建立长连接:服务端设置响应头 Content-Type: text/event-stream、Cache-Control: no-cache、Connection: keep-alive,保持连接不关闭。
- 流式数据推送:服务端按 data: 内容\n\n 格式持续发送数据。
- 前端实时解析:前端监听 message 事件,逐段接收并实时渲染数据。
- 连接关闭:数据传输完成时服务端发送结束标识,或由任意一方主动关闭。
3.2 消息格式
SSE 的消息格式非常简洁:
event:userupdateid:12345data:{"username":"john_doe","status":"online"}关键字段说明:
- data::实际的消息内容(必需)
- event::事件类型(可选,默认为 message)
- id::消息唯一标识,用于断点续传
- retry::断线重连前的等待时间(毫秒)
每条消息以两个换行符 \n\n 结尾。
四、代码示例
4.1 基础用法:前端客户端
浏览器原生提供了 EventSource API,使用非常简单。
检测浏览器兼容性:
if('EventSource'inwindow){console.log('浏览器支持 SSE');}else{console.log('浏览器不支持 SSE,需降级处理');}创建连接与监听事件:
// 建立 SSE 连接consteventSource=newEventSource('/api/sse/stream');// 连接建立成功eventSource.onopen=function(event){console.log('SSE 连接已建立');};// 接收消息(默认事件)eventSource.onmessage=function(event){constdata=JSON.parse(event.data);console.log('收到数据:',data);// 更新页面内容...};// 处理连接错误eventSource.onerror=function(event){console.error('SSE 连接出错');};// 监听自定义事件eventSource.addEventListener('notification',function(event){constdata=JSON.parse(event.data);console.log('收到通知:',data);});// 主动关闭连接// eventSource.close();跨域携带 Cookie:
constsource=newEventSource(url,{withCredentials:true// 跨域请求携带 Cookie});4.2 场景一:AI 大模型流式响应
这是 SSE 目前最热门的应用场景之一。下面展示如何实现类似 ChatGPT 的“打字机”效果。
后端示例(Node.js + Express):
constexpress=require('express');constapp=express();app.get('/api/ai/stream',(req,res)=>{// 设置 SSE 必需的响应头res.setHeader('Content-Type','text/event-stream');res.setHeader('Cache-Control','no-cache');res.setHeader('Connection','keep-alive');// 模拟 AI 逐字生成consttext='你好,欢迎使用 AI 助手!';letindex=0;constinterval=setInterval(()=>{if(index<text.length){// 逐个字符推送res.write(`data:${JSON.stringify({char:text[index],done:false})}\n\n`);index++;}else{// 发送结束标识res.write(`data:${JSON.stringify({done:true})}\n\n`);clearInterval(interval);res.end();}},100);// 客户端断开时清理资源req.on('close',()=>{clearInterval(interval);res.end();});});app.listen(3000);前端实现:
consteventSource=newEventSource('/api/ai/stream');letoutput='';eventSource.onmessage=function(event){constdata=JSON.parse(event.data);if(data.done){console.log('AI 回复完成');eventSource.close();return;}// 逐字追加,实现打字机效果output+=data.char;document.getElementById('ai-output').textContent=output;};eventSource.onerror=function(){console.error('AI 流式连接中断');};4.3 场景二:文件下载进度推送
大文件下载或生成时,通过 SSE 实时推送进度信息。
后端示例(Node.js):
app.get('/api/download/progress',(req,res)=>{res.setHeader('Content-Type','text/event-stream');res.setHeader('Cache-Control','no-cache');res.setHeader('Connection','keep-alive');letprogress=0;constinterval=setInterval(()=>{progress+=Math.random()*10;if(progress>=100){progress=100;res.write(`data:${JSON.stringify({progress,status:'complete'})}\n\n`);clearInterval(interval);res.end();}else{res.write(`data:${JSON.stringify({progress,status:'downloading'})}\n\n`);}},500);req.on('close',()=>{clearInterval(interval);res.end();});});前端实现:
consteventSource=newEventSource('/api/download/progress');constprogressBar=document.getElementById('progress-bar');conststatusText=document.getElementById('status-text');eventSource.onmessage=function(event){constdata=JSON.parse(event.data);// 更新进度条progressBar.style.width=data.progress+'%';progressBar.textContent=data.progress+'%';if(data.status==='complete'){statusText.textContent='✅ 下载完成!';eventSource.close();// 触发实际文件下载...}else{statusText.textContent=`⏳ 下载中${Math.round(data.progress)}%`;}};实践建议:进度更新应进行节流(throttle),避免过于频繁地推送消息导致前端性能问题。
五、最佳实践与注意事项
5.1 连接稳定性保障
心跳保活:如果服务器超过约 30 秒没有数据推送,建议发送心跳消息(如 data: ping\n\n)保持连接活跃,避免被代理或网关超时断开。
自动重连:浏览器默认在连接断开后 3 秒自动重连,可通过 retry 字段自定义间隔。
断点续传:利用 Last-Event-ID 机制,客户端重连时可告知服务器上次收到的消息 ID,实现断点续传。
5.2 服务端配置要点
// SSE 必需的响应头res.setHeader('Content-Type','text/event-stream');res.setHeader('Cache-Control','no-cache');res.setHeader('Connection','keep-alive');5.3 Nginx 反向代理注意事项
使用 Nginx 代理 SSE 时,务必禁用代理缓冲,否则数据会被缓冲到一定大小才发送,破坏实时性:
location /sse/ { proxy_pass http://backend; proxy_buffering off; proxy_cache off; proxy_set_header Connection ''; proxy_http_version 1.1; chunked_transfer_encoding off; }5.4 连接数限制
HTTP/1.1 下,浏览器对同一域名有 6 个连接的限制。如果需要同时维持大量 SSE 连接,建议使用 HTTP/2 或多域名部署。
5.5 安全建议
使用 HTTPS 加密通信
对 SSE 端点进行身份验证
设置合理的超时时间
5.6 错误处理与降级
监听 error 事件并实现指数退避重试策略:
letretryCount=0;constmaxRetries=5;functionconnectSSE(){constsource=newEventSource('/api/sse');source.onerror=function(){source.close();if(retryCount<maxRetries){constdelay=Math.min(1000*Math.pow(2,retryCount),30000);setTimeout(()=>{retryCount++;connectSSE();},delay);}};}六、总结
SSE(Server-Sent Events)是一项成熟、轻量且易于实现的服务器推送技术。它的核心价值在于:
- 简单易用:基于标准 HTTP 协议,无需额外协议升级,浏览器原生支持
- 自动重连:内置断线重连机制,开箱即用
- 生态兼容:天然适配现有的 HTTP 基础设施(代理、负载均衡、防火墙)
- 资源高效:协议开销低,单连接内存占用小
在 AI 大模型流式响应、实时通知、数据看板等需要服务器单向推送的场景中,SSE 往往是比 WebSocket 更务实的选择。它不像 WebSocket 那样功能强大,但正因其“克制”,才换来了简单、稳定和低门槛——而这正是大多数实时推送场景最需要的品质。
掌握 SSE,意味着你可以在不引入复杂技术栈的情况下,为用户带来流畅的实时体验。