為什麼ChatGPT採用SSE協議而不是Websocket?

人工智慧洞察站發表於2024-01-22

來源:程式新視界

在探索ChatGPT的使用過程中,我們發現GPT採用了流式資料返回的方式。理論上,這種情況可以透過全雙工通訊協議實現持久化連線,或者依賴於基於EventStream的事件流。然而,ChatGPT選擇了後者,也就是本文即將深入探討的SSE(Server-Sent Events)技術。

要理解這個選擇,我們需要關注ChatGPT的使用場景。作為一個基於深度學習的大型語言模型,ChatGPT需要處理大量的自然語言資料,這無疑需要大量的計算資源和時間。相較於普通的讀取資料庫操作,其響應速度自然會慢許多。

對於這種可能需要長時間等待響應的對話場景,ChatGPT採用了一種巧妙的策略:它會將已經計算出的資料“推送”給使用者,並利用SSE技術在計算過程中持續返回資料。這樣做可以避免使用者因等待時間過長而選擇關閉頁面。

什麼是 SSE?

SSE(Server-Sent Events)是一種Web技術,它允許伺服器實時向客戶端推送資料。相比於傳統的輪詢和長輪詢機制,SSE提供了一種更高效且實時的資料推送方式。這種技術主要應用於構建實時應用,例如實時訊息推送、股票行情更新等。

SSE是HTML5規範中的一個通訊相關API,它主要包含兩個部分:服務端與瀏覽器端的通訊協議(基於HTTP協議),以及瀏覽器端JavaScript可使用的EventSource物件。

SSE執行在HTTP協議之上,它允許伺服器以事件流(Event Stream)的形式將資料傳送給客戶端。客戶端透過建立持久化的HTTP連線,並監聽這個事件流,從而可以實時接收到伺服器推送的資料。

SSE具有以下幾個主要特點:

  • 簡單易用:SSE使用基於文字的資料格式,如純文字、JSON等,這使得資料傳送和解析都相對簡單直接。
  • 單向通訊:SSE僅支援從伺服器到客戶端的單向通訊。這意味著伺服器可以主動推送資料給客戶端,但客戶端只能被動接收資料。
  • 實時性:由於SSE能夠建立持久化連線,伺服器因此可以實時地將資料推送給客戶端,無需客戶端頻繁地發起請求。這大大提高了資料傳輸的效率和實時性。

SSE與WebSocket的比較

WebSocket是一種Web技術,用於實現實時雙向通訊,它與SSE(Server-Sent Events)在某些方面存在差異。以下是對兩者的比較:

  • 資料推送方向:SSE主要支援從伺服器到客戶端的單向通訊,這意味著伺服器可以主動地向客戶端推送資料。而WebSocket則支援雙向通訊,允許伺服器和客戶端之間進行實時的資料交換。
  • 連線建立:SSE利用基於HTTP的長連線,透過常規的HTTP請求和響應來建立連線,進而實現資料的實時推送。相反,WebSocket採用自定義的協議,透過建立WebSocket連線來實現雙向通訊。
  • 相容性:由於SSE基於HTTP協議,因此它可以在大多數現代瀏覽器中使用,並且無需進行額外的協議升級。雖然WebSocket在絕大多數現代瀏覽器中也得到了支援,但在某些特定的網路環境下可能會遇到問題。
  • 適用場景:SSE適合於需要伺服器向客戶端實時推送資料的場景,例如股票價格更新、新聞實時推送等。而WebSocket則適合於需要實時雙向通訊的場景,如聊天應用、多人線上協作編輯等。

選擇使用SSE還是WebSocket主要取決於具體的業務需求和場景。如果你只需要實現從伺服器向客戶端的單向資料推送,並且希望保持操作簡便且相容性好,那麼SSE是一個理想的選擇。然而,如果你需要實現雙向通訊,或者需要更高階的功能和控制,那麼WebSocket可能會更適合你的需求。

SSE的實現原理

以下是SSE(Server-Sent Events)的實現原理:

  • 連線建立:通常情況下,客戶端(如瀏覽器)透過傳送HTTP GET請求到伺服器來請求建立一個SSE連線。
  • 伺服器響應:一旦伺服器接收到請求,它將返回一個HTTP響應,該響應的狀態碼為200,內容型別(Content-Type)設定為"text/event-stream"。
  • 資料推送:伺服器可以透過已經建立的連線向客戶端推送資料。每次推送的資料被稱作一個事件(Event)。每個事件由一個或多個以"\n\n"分隔的資料塊組成。每個資料塊都是一行文字,可能包含一個以":"開頭的註釋行、以"data:"開頭的資料行,或者以"id:"和"event:"開頭的行來指定事件ID和事件型別。
  • 客戶端處理:當客戶端接收到伺服器推送的事件後,它會觸發相應的JavaScript事件處理器來處理這些事件。
  • 重連:如果連線斷開,客戶端會自動嘗試重新連線。如果伺服器在事件中指定了ID,那麼在重新連線時,客戶端會傳送一個"Last-Event-ID"的HTTP頭部資訊到伺服器,告訴伺服器客戶端接收到的最後一個事件的ID。根據這個資訊,伺服器可以決定從哪個事件開始重新傳送資料。

總結起來,SSE使用了基於文字和HTTP協議的簡單機制,使得伺服器能夠實時地將資料推送到客戶端,而無需客戶端頻繁地發起新的請求。

使用SSE的注意事項

以下是在使用SSE(Server-Sent Events)技術進行實時資料推送時需要注意的幾個關鍵點:

  • 非同步處理:由於SSE基於長連線的機制,因此資料推送過程可能會持續較長時間。為了防止伺服器執行緒被阻塞,建議採用非同步方式處理SSE請求。例如,可以在控制器方法中使用@Async註解或利用CompletableFuture等非同步程式設計方式。
  • 超時處理:SSE連線可能會因網路中斷、客戶端關閉等原因而超時。為了避免無效連線佔據伺服器資源,建議設定超時時間並處理超時情況。例如,可以利用SseEmitter物件的setTimeout()方法設定超時時間,並透過onTimeout()方法處理超時邏輯。
  • 異常處理:在實際應用中,可能會遇到網路異常、資料推送失敗等問題。這種情況下,可以使用SseEmitter物件的completeWithError()方法將異常資訊傳送給客戶端,並在客戶端透過eventSource.onerror事件進行處理。
  • 記憶體管理:在使用SseEmitter時,需要特別注意記憶體管理問題,尤其是在大量併發連線的場景下。當客戶端斷開連線後,務必及時釋放SseEmitter物件,以避免資源洩漏和記憶體溢位。
  • 併發效能:SSE的併發連線數可能對伺服器效能產生影響。如果需要處理大量併發連線,可以考慮使用執行緒池或其他非同步處理方式,以最大化伺服器資源利用。
  • 客戶端相容性:雖然大多數現代瀏覽器都支援SSE,但一些舊版本的瀏覽器可能不支援。因此,在使用SSE時,需要確保目標客戶端對其有良好的支援,或者提供備選的實時資料推送機制。

以上這些注意事項可以根據具體應用需求進行調整和最佳化。在實際應用中,確保伺服器的穩定性、安全性和效能是非常重要的。同時,在處理SSE連線時,可以考慮適當的限流和安全控制措施,以防止濫用和惡意連線的出現。總的來說,使用SSE技術時需要全面考慮各個方面的因素,才能實現高效、穩定、安全的實時資料推送服務。

SpringBoot整合SSE案例

假設正在開發一個實時股票價格監控應用,需要將股票價格實時推送給客戶端。以下為Spring Boot中整合SSE技術實現的場景示例程式碼。

首先,定義一個控制器來處理SSE請求和傳送實時股票價格:

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.util.Random;

@RestController
public class StockController {

    @GetMapping(value = "/stock-price", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter streamStockPrice() {
        SseEmitter emitter = new SseEmitter();
        // 模擬生成實時股票價格並推送給客戶端
        Random random = new Random();
        new Thread(() -> {
            try {
                while (true) {
                    // 生成隨機的股票價格
                    double price = 100 + random.nextDouble() * 10;
                    // 構造股票價格的訊息
                    String message = String.format("%.2f", price);
                    // 傳送訊息給客戶端
                    emitter.send(SseEmitter.event().data(message));
                    // 休眠1秒鐘
                    Thread.sleep(1000);
                }
            } catch (Exception e) {
                emitter.completeWithError(e);
            }
        }).start();
        return emitter;
    }
}

在上述程式碼中,定義了一個streamStockPrice()方法,該方法使用@GetMapping註解將/stock-price路徑對映到該方法上,並指定produces = MediaType.TEXT_EVENT_STREAM_VALUE以表明該方法將產生SSE事件流。

在方法內部建立了一個SseEmitter物件作為事件發射器,並在一個單獨的執行緒中不斷生成隨機的股票價格,並將價格轉換為字串形式傳送給客戶端。

透過emitter.send()方法傳送的資料會被封裝為SSE事件流的形式,客戶端可以透過監聽該事件流來實時接收股票價格。

在前端頁面中,建立一個簡單的HTML頁面來展示實時股票價格:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>實時股票價格監控</title>
  </head>
  <body>
    <h1>實時股票價格</h1>
    <div id="stock-price"></div>

    <script>
      const eventSource = new EventSource('/stock-price');
      eventSource.onmessage = function (event{
        document.getElementById('stock-price').innerHTML = event.data;
      };
    
</script>
  </body>
</html>

上述程式碼中,透過new EventSource('/stock-price')建立了一個EventSource物件,它與/stock-price路徑建立SSE連線。然後,透過eventSource.onmessage定義了接收訊息的回撥函式,在收到新訊息時更新頁面上的股票價格。

透過以上程式碼,可以在瀏覽器中開啟該HTML頁面,它會建立與伺服器的SSE連線,並實時接收並展示股票價格。這只是使用SSE實現實時資料推送的一個簡單示例。在實踐中,可以根據具體的業務需求和場景,進行更復雜和豐富的實現。

小結

SSE(Server-Sent Events)是一種基於HTTP協議的輕量級實時通訊技術,具備服務端推送、斷線重連和簡單輕量等優點。然而,它也存在一些限制,例如無法進行雙向通訊、連線數受限以及僅支援GET請求等。

在Web應用程式中,SSE可以實現各種即時資料推送功能,如股票線上資料更新、日誌推送、實時顯示聊天室人數等。

然而,需要注意的是,並非所有的實時推送場景都適合使用SSE。在需要處理高併發、高吞吐量和低延遲的場景下,WebSocket可能是更好的選擇。而對於那些需要輕量級推送解決方案的場景,SSE可能會更加適合。

因此,在選擇實時更新方案時,我們需要根據具體的需求和應用場景來做出決策。只有這樣,我們才能確保選擇的技術能夠最大程度地滿足我們的需求。

來自 “ ITPUB部落格 ” ,連結:https://blog.itpub.net/70027828/viewspace-3004611/,如需轉載,請註明出處,否則將追究法律責任。

相關文章