SSE:輕量級實時資料推送神器

FunTester發表於2025-02-22

介紹

在現代 Web 開發中,實時資料推送已經成為許多應用的核心需求。無論是股票行情、社交媒體通知,還是線上協作編輯,使用者都希望能夠即時獲取最新的資訊。在這種背景下,伺服器傳送事件(Server-Sent Events,SSE)作為一種輕量級的實時通訊技術,提供了一種簡單而高效的解決方案。

什麼是伺服器傳送事件

伺服器傳送事件(SSE)是一種基於 HTTP 協議的單向通訊技術,允許伺服器透過持久連線向客戶端持續推送資料。它使用EventSource API 來接收資料,伺服器透過text/event-stream格式傳送訊息。這種方式特別適合需要實時更新資料的應用場景,例如新聞推送、線上監控、社交媒體通知等。

SSE 的適用場景:

  • 金融資料更新:如股票市場價格變化。
  • 社交媒體:實時訊息流。
  • 日誌系統:監控和分析日誌流。
  • 實時通知:如郵件提醒、任務更新。
  • 協作工具:如文件協作編輯。

與 WebSockets 相比,SSE 更適合單向資料流的場景。它直接基於 HTTP 協議,無需額外的協議支援,因此更加輕量級。

SSE 的主要優點

  1. 簡單易用: SSE 直接基於 HTTP 協議,前端可以透過EventSource輕鬆接收資料,無需複雜的配置或額外的伺服器支援。
  2. 自動重連: 瀏覽器原生支援 SSE 連線斷開後的自動重連機制,無需手動實現心跳檢測或重連邏輯。
  3. 低資源消耗: SSE 執行在 HTTP 長連線之上,不會佔用額外的 TCP 埠,也沒有額外的握手開銷,適合大多數 Web 伺服器。
  4. 相容性好: 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))
}

程式碼解析

  1. Content-Type: text/event-stream —— 確保瀏覽器識別為 SSE 連線。
  2. Flusher.Flush() —— 立即推送資料到客戶端,確保資料流不會被緩衝。
  3. for 迴圈 —— 持續傳送資料,每 2 秒推送一次時間資訊。
  4. 跨域支援 —— Access-Control-Allow-Origin: *允許跨域訪問。
  5. 錯誤處理 —— 如果客戶端斷開連線,日誌記錄並停止推送資料。

前端(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 是最簡單、最穩定的選擇。

專業提示

  1. 最佳化長連線:預設情況下,SSE 連線會一直保持開啟狀態。建議伺服器設定keep-alive以防止超時斷開。
  2. 負載均衡:SSE 依賴 HTTP 長連線,不適合大規模併發,建議結合 Nginx 負載均衡使用,如proxy_buffering off;確保流式傳輸。
  3. 資料恢復機制:使用Last-Event-ID允許客戶端在斷開後重新獲取丟失的資料。
  4. 跨域支援:如果伺服器與前端域名不同,需要設定 CORS 允許跨域訪問。
res.setHeader('Access-Control-Allow-Origin', '*');

結論

SSE 是一種輕量級、易實現的實時資料推送方案,適用於單向資料流場景,如股票市場、新聞推送、社交媒體通知等。相較於 WebSockets,SSE 更簡單,瀏覽器原生支援自動重連,不需要額外的協議或伺服器負擔。

如果你的應用只需要伺服器推送資料到客戶端,SSE 是一個理想的選擇。而如果你需要雙向實時通訊,WebSockets 可能更合適。正確選擇技術,才能讓應用更加高效和穩定。

FunTester 原創精華

【連載】從 Java 開始效能測試

  • 故障測試與 Web 前端
  • 服務端功能測試
  • 效能測試專題
  • Java、Groovy、Go
  • 白盒、工具、爬蟲、UI 自動化
  • 理論、感悟、影片
如果覺得我的文章對您有用,請隨意打賞。您的支援將鼓勵我繼續創作!
打賞支援
暫無回覆。

相關文章