介紹
在現代 Web 開發中,實時資料推送已經成為許多應用的核心需求。無論是股票行情、社交媒體通知,還是線上協作編輯,使用者都希望能夠即時獲取最新的資訊。在這種背景下,伺服器傳送事件(Server-Sent Events,SSE)作為一種輕量級的實時通訊技術,提供了一種簡單而高效的解決方案。
什麼是伺服器傳送事件
伺服器傳送事件(SSE)是一種基於 HTTP 協議的單向通訊技術,允許伺服器透過持久連線向客戶端持續推送資料。它使用EventSource
API 來接收資料,伺服器透過text/event-stream
格式傳送訊息。這種方式特別適合需要實時更新資料的應用場景,例如新聞推送、線上監控、社交媒體通知等。
SSE 的適用場景:
- 金融資料更新:如股票市場價格變化。
- 社交媒體:實時訊息流。
- 日誌系統:監控和分析日誌流。
- 實時通知:如郵件提醒、任務更新。
- 協作工具:如文件協作編輯。
與 WebSockets 相比,SSE 更適合單向資料流的場景。它直接基於 HTTP 協議,無需額外的協議支援,因此更加輕量級。
SSE 的主要優點
- 簡單易用: SSE 直接基於 HTTP 協議,前端可以透過
EventSource
輕鬆接收資料,無需複雜的配置或額外的伺服器支援。 - 自動重連: 瀏覽器原生支援 SSE 連線斷開後的自動重連機制,無需手動實現心跳檢測或重連邏輯。
- 低資源消耗: SSE 執行在 HTTP 長連線之上,不會佔用額外的 TCP 埠,也沒有額外的握手開銷,適合大多數 Web 伺服器。
- 相容性好: SSE 適用於所有支援 HTTP 的環境,包括 CDN 和代理伺服器,並且可以結合快取策略最佳化效能。
SSE 的訊息格式
SSE 採用純文字格式傳送資料,每條訊息以換行符\n\n
結束。訊息格式如下:
data: 這是一條普通訊息
data: {"name": "John", "message": "Hello"}
SSE 還支援自定義事件型別,客戶端可以監聽不同型別的訊息:
event: update
data: {"status": "success", "timestamp": "2025-02-13T12:00:00Z"}
event: alert
data: {"message": "系統異常"}
在客戶端,可以使用addEventListener
監聽特定事件:
eventSource.addEventListener("update", (event) => {
console.log("更新訊息:", event.data);
});
伺服器還可以透過id
欄位提供斷點恢復功能。客戶端在重連時會自動帶上Last-Event-ID
,伺服器可以據此恢復訊息流:
id: 12345
data: 這是一條可以恢復的訊息
Show You Code
以下是一個完整的 SSE 伺服器和前端程式碼示例。
伺服器端(Go 示例)
package main
import (
"fmt"
"log"
"net/http"
"time"
)
// SSE處理函式
func sseHandler(w http.ResponseWriter, r *http.Request) {
// 設定SSE必要的HTTP頭
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
w.Header().Set("Access-Control-Allow-Origin", "*") // 允許跨域
// 獲取寫入流
flusher, ok := w.(http.Flusher)
if !ok {
http.Error(w, "Streaming unsupported", http.StatusInternalServerError)
return
}
// 定期傳送資料
for {
_, err := fmt.Fprintf(w, "FunTester data: 當前時間:%s\n\n", time.Now().Format(time.RFC3339))
if err != nil {
log.Println("FunTester 客戶端連線斷開:", err)
break
}
flusher.Flush() // 立即推送資料到客戶端
time.Sleep(2 * time.Second)
}
}
func main() {
http.HandleFunc("/events", sseHandler)
port := 8080
log.Printf("SSE伺服器執行在 FunTester http://localhost:%d/events", port)
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))
}
程式碼解析
- Content-Type: text/event-stream —— 確保瀏覽器識別為 SSE 連線。
- Flusher.Flush() —— 立即推送資料到客戶端,確保資料流不會被緩衝。
- for 迴圈 —— 持續傳送資料,每 2 秒推送一次時間資訊。
- 跨域支援 ——
Access-Control-Allow-Origin: *
允許跨域訪問。 - 錯誤處理 —— 如果客戶端斷開連線,日誌記錄並停止推送資料。
前端(JavaScript 客戶端)
const eventSource = new EventSource('/events');
eventSource.onmessage = (event) => {
console.log("收到訊息:", event.data);
};
eventSource.onerror = () => {
console.log("FunTester 連線丟失,嘗試重連...");
};
瀏覽器會自動維護 SSE 連線,並在斷開時嘗試重新連線。
SSE 與 WebSockets 的對比
SSE 和 WebSockets 都能實現實時資料推送,但它們的設計目標不同。
特性 | SSE | WebSocket |
---|---|---|
通訊方式 | 單向(伺服器 → 客戶端) | 雙向(客戶端 ↔ 伺服器) |
協議 | 基於 HTTP(EventStream) | 自定義 TCP 協議 |
自動重連 | 瀏覽器內建支援 | 需要手動實現 |
相容性 | 相容 HTTP 代理、CDN | 可能受限於防火牆、代理 |
適用場景 | 伺服器資料推送(新聞、日誌) | 聊天、遊戲、協作編輯 |
如果應用只需要伺服器向客戶端推送資料(如股票行情、新聞、社交通知),SSE 是更好的選擇。如果需要雙向互動(如線上遊戲、WebRTC、IM 聊天),WebSockets 更適合。
SSE 的最佳用例
SSE 在以下場景中表現出色:
- 實時資料流:如日誌監控、金融資料。
- 社交媒體推送:如 Twitter、Facebook。
- 訊息通知系統:如郵件提醒、新訂單提醒。
- 物聯網裝置監控:如 IoT 感測器資料。
- 多人協作系統:如 Google Docs、Figma。
如果應用主要是伺服器向客戶端推送資料,SSE 是最簡單、最穩定的選擇。
專業提示
- 最佳化長連線:預設情況下,SSE 連線會一直保持開啟狀態。建議伺服器設定
keep-alive
以防止超時斷開。 - 負載均衡:SSE 依賴 HTTP 長連線,不適合大規模併發,建議結合 Nginx 負載均衡使用,如
proxy_buffering off;
確保流式傳輸。 - 資料恢復機制:使用
Last-Event-ID
允許客戶端在斷開後重新獲取丟失的資料。 - 跨域支援:如果伺服器與前端域名不同,需要設定 CORS 允許跨域訪問。
res.setHeader('Access-Control-Allow-Origin', '*');
結論
SSE 是一種輕量級、易實現的實時資料推送方案,適用於單向資料流場景,如股票市場、新聞推送、社交媒體通知等。相較於 WebSockets,SSE 更簡單,瀏覽器原生支援自動重連,不需要額外的協議或伺服器負擔。
如果你的應用只需要伺服器推送資料到客戶端,SSE 是一個理想的選擇。而如果你需要雙向實時通訊,WebSockets 可能更合適。正確選擇技術,才能讓應用更加高效和穩定。
FunTester 原創精華
【連載】從 Java 開始效能測試
- 故障測試與 Web 前端
- 服務端功能測試
- 效能測試專題
- Java、Groovy、Go
- 白盒、工具、爬蟲、UI 自動化
- 理論、感悟、影片