Server-Sent Events(SSE)
- SSE 的特點
- 1. 單向通信
- 2. 簡單易用,瀏覽器原生支持
- 3. 持久連接
- 4. 純文本傳輸
- 5. 自動重連機制
- 6. 輕量級協議
- SSE 的實現
- 服務器端實現(Node.js 示例)
- 1. HTTP 響應頭設置
- 2. 數據推送模式
- 3. 服務器端代碼示例
- 數據格式規范詳解
- 1. 完整事件結構
- 2. 錯誤處理示例
- 客戶端實現(HTML + JavaScript)
- HTML + JavaScript 代碼示例
- 高級功能
- SSE 與 WebSocket 的比較
- 混合架構實踐
- SSE 的應用場景
- 安全與性能優化
- 1. 安全防護
- 2. 性能調優
- SSE擴展方案
- 1. 斷線續傳實現
- 2. 二進制數據傳輸
- 總結
在現代 Web 應用中,實時數據推送成為了關鍵需求之一。例如,在股票行情、天氣更新、社交通知等應用場景中,客戶端需要能夠持續接收服務器端的最新數據。Server-Sent Events (SSE) 是一種基于 HTTP 協議的輕量級實時通信技術,能夠讓服務器主動向客戶端推送消息。
下一節分享了「WebSocket:高效的雙向實時通信技術」
SSE 的特點
1. 單向通信
SSE 是服務器向客戶端推送數據的單向通道。客戶端可以持續接收服務器端的更新,但不會主動向服務器發送數據(除非建立額外的請求)。
2. 簡單易用,瀏覽器原生支持
SSE 使用簡單的文本格式傳輸事件,大多數現代瀏覽器(如 Chrome、Firefox、Edge 和 Safari)都提供了內置支持,無需額外的庫或插件。
3. 持久連接
SSE 采用持久 HTTP 連接(HTTP 長連接),服務器可以連續發送數據流,而無需客戶端重復發送請求。
4. 純文本傳輸
SSE 傳輸的數據是純文本格式,消息之間以換行符 (\n\n
) 分隔,便于解析和調試。
5. 自動重連機制
如果 SSE 連接因網絡故障等原因斷開,瀏覽器會自動嘗試重新連接服務器,而無需額外處理。
const es = new EventSource('/sse');
es.onerror = () => { /* 處理中斷 */ };
- 底層原理: SSE協議規范中定義了客戶端自動重連機制,當連接異常斷開時,瀏覽器默認以指數退避策略(初始約3秒)嘗試重新連接
- 開發優勢: 相比WebSocket需手動實現斷線重連,SSE顯著降低實時應用開發復雜度
- 注意事項: 可通過
retry:
字段指定重試間隔(單位:毫秒ms)
6. 輕量級協議
- 協議開銷: 基于純文本的簡單協議格式,每個消息僅增加約20字節頭部信息
- 傳輸效率: 適用于高頻小數據量場景(如實時股票報價),避免WebSocket的握手開銷
SSE 的實現
服務器端實現(Node.js 示例)
在服務器端,SSE 通過正確的 HTTP 頭部設置來標識事件流,并以特定格式發送數據。
1. HTTP 響應頭設置
res.writeHead(200, {'Content-Type': 'text/event-stream', // 必須聲明SSE類型'Cache-Control': 'no-cache', // 禁用瀏覽器緩存'Connection': 'keep-alive', // 保持長連接'Access-Control-Allow-Origin': '*' // CORS配置
});
- 關鍵頭信息:
Content-Type
必須為text/event-stream
Cache-Control: no-cache
防止代理服務器緩存X-Accel-Buffering: no
(Nginx環境禁用緩沖)
2. 數據推送模式
setInterval(() => {res.write(`data: ${JSON.stringify(data)}\n\n`);
}, 1000);
- 流式寫入: 必須通過分塊編碼(chunked encoding)持續發送數據
- 消息邊界: 每條消息必須以
\n\n
結尾,字段間用\n
分隔 - 性能優化: 使用寫緩沖區,避免頻繁的TCP包發送
3. 服務器端代碼示例
const http = require('http');http.createServer((req, res) => {res.writeHead(200, {'Content-Type': 'text/event-stream','Cache-Control': 'no-cache','Connection': 'keep-alive'});let clientId = 0;const retryInterval = 5000; // 5秒自動重連const eventSender = setInterval(() => {clientId++;const message = `id: ${clientId}\nevent: update\nretry: ${retryInterval}\ndata: This is message number ${clientId}\n\n`;res.write(message);}, 1000);req.on('close', () => {clearInterval(eventSender);});}).listen(3000, () => {console.log('SSE server running at http://localhost:3000/');
});
數據格式規范詳解
1. 完整事件結構
event: userUpdate\n
data: {"id": 101, "status": "online"}\n
id: 101-20230301\n
retry: 10000\n\n
event
:自定義事件類型,觸發客戶端對應事件監聽器data
:支持多行數據(每行需以data:
開頭)- 建議JSON格式傳輸結構化數據
id
:事件ID,用于斷線續傳(Last-Event-ID請求頭)retry
:控制重連間隔(單位:毫秒)
2. 錯誤處理示例
res.write(`event: error\ndata: 服務端異常\n\n`);
- 客戶端通過監聽
error
事件進行異常處理 - 建議包含錯誤代碼和描述信息的JSON數據
客戶端實現(HTML + JavaScript)
在前端,使用 EventSource
API 輕松實現 SSE 連接。
HTML + JavaScript 代碼示例
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>SSE Demo</title><script>document.addEventListener("DOMContentLoaded", function () {const eventSource = new EventSource('http://localhost:3000/api/notifications');// 自定義事件處理eventSource.addEventListener('update', function(event) {console.log('Received update:', event.data);});// 默認消息類型監聽eventSource.onmessage = function(event) {console.log('General message:', event.data);};// 錯誤處理eventSource.onerror = function(err) {console.error('EventSource failed:', err);};});</script>
</head>
<body><h1>Server-Sent Events Demo</h1><p>Check the console for messages.</p>
</body>
</html>
高級功能
// 自定義請求頭(需CORS支持)
const es = new EventSource('/api/stream', {withCredentials: true
});// 連接狀態管理
es.onopen = () => console.log('連接已建立');
es.onerror = (e) => {if (e.target.readyState === EventSource.CLOSED) {console.log('連接永久關閉');}
};// 主動關閉連接
document.getElementById('stop').onclick = () => es.close();
SSE 與 WebSocket 的比較
特性 | SSE | WebSocket |
---|---|---|
協議 | HTTP長連接 | 獨立的ws/wss協議 |
壓縮支持 | 支持gzip/brotli壓縮 | 需要擴展實現 |
消息延遲 | 較高(依賴HTTP層) | 低延遲(二進制幀) |
連接方式 | 服務器推送數據到客戶端 | 客戶端和服務器可雙向通信 |
適用場景 | 服務器需要頻繁更新數據,如新聞推送、日志更新、實時儀表盤(如監控系統)、新聞/社交媒體Feed流、長輪詢替代方案等 | 需要實時交互,如聊天應用、多人協作、高頻雙向通信(如在線游戲)等 |
瀏覽器支持 | 原生支持,大多數現代瀏覽器均支持 | 需要 WebSocket API,部分老舊瀏覽器不支持 |
連接管理 | 自動管理重新連接 | 需手動處理連接丟失與重連 |
混合架構實踐
// 組合使用案例:SSE推送通知 + WebSocket實現聊天
const notificationStream = new EventSource('/notifications');
const chatSocket = new WebSocket('wss://chat.example.com');// 消息類型路由處理
function handleMessage(data) {if (data.type === 'notification') {// 使用SSE處理} else if (data.type === 'chat') {// 使用WebSocket處理 }
}
SSE 的應用場景
SSE 適用于以下應用場景:
- 實時新聞推送:新聞網站可使用 SSE 向用戶推送最新的新聞資訊。
- 股票行情更新:股票交易平臺可以用 SSE 傳輸市場行情數據。
- 服務器日志實時展示:開發者工具可以通過 SSE 實時顯示服務器日志。
- 社交通知:社交平臺可利用 SSE 向用戶推送好友動態或點贊通知。
安全與性能優化
1. 安全防護
// 身份驗證示例(Cookie + CORS)
app.use('/secure-stream', (req, res, next) => {if (!validateToken(req.cookies.token)) {return res.status(401).end();}next();
});
- 認證方案: Cookie驗證、JWT令牌、OAuth2.0
- 安全頭設置:
Content-Security-Policy: default-src 'self'
X-Content-Type-Options: nosniff
2. 性能調優
-
服務端優化:
- 連接數限制(Nginx worker_connections)
- 心跳機制保持連接活躍
setInterval(() => {res.write(':ping\n\n'); // 注釋行作為心跳 }, 30000);
-
客戶端優化:
- 合理設置EventSource并發數
- 及時關閉不需要的連接
SSE擴展方案
1. 斷線續傳實現
// 服務端記錄最后事件ID
let lastId = 0;
app.get('/resumable-stream', (req, res) => {const clientLastId = req.headers['last-event-id'] || 0;// 從clientLastId之后開始發送數據
});
2. 二進制數據傳輸
// 通過Base64編碼傳輸
const buffer = await getImageBuffer();
res.write(`data: ${buffer.toString('base64')}\n\n`);// 客戶端解碼
es.onmessage = (e) => {const img = document.createElement('img');img.src = `data:image/png;base64,${e.data}`;
};
總結
SSE 是一種輕量級的服務器推送技術,適用于服務器單向推送數據的場景。它基于 HTTP,簡單易用,支持持久連接和自動重連,廣泛應用于實時數據更新場景。盡管相比 WebSocket 功能有限,但對于不需要雙向通信的應用,SSE 提供了更簡單、更高效的解決方案。
如果你的應用場景涉及實時數據推送,而不需要客戶端主動發送消息,那么 SSE 可能是一個理想的選擇!