一文帶你學會SSE,你學會了嗎?
隨著Web應用的不斷發(fā)展,實時數(shù)據(jù)傳輸?shù)男枨笞兊迷絹碓狡毡?。傳統(tǒng)的輪詢方法不僅效率低下,而且在高并發(fā)情況下會對服務器造成不必要的壓力。為了解決這個問題,Server-Sent Events (SSE) 應運而生,它允許服務器端主動向客戶端推送更新。
Server-Sent Events
Server-Sent Events 是一種允許服務器向瀏覽器發(fā)送實時更新的技術。不同于WebSocket的全雙工通信方式,SSE更專注于單向的數(shù)據(jù)流,即從服務器到客戶端的數(shù)據(jù)推送。
這種方式對于需要實時更新的場景非常有用,當前主流的大模型平臺,比如ChatGPT、通義千問、文心一言,對話時采用的就是SSE。
SSE 本質是一個基于 http 協(xié)議的通信技術。
SSE的應用
引入依賴
spring-boot-starter-web 中默認已經引用了 sse,所以我們不需要額外引入其他依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
基本用法
@RestController
public class SseController {
@GetMapping("/sse")
public SseEmitter handleSse() {
SseEmitter emitter = new SseEmitter();
// 異步處理發(fā)送事件
new Thread(() -> {
try {
// 推送事件
emitter.send("實時消息:你好!");
Thread.sleep(1000); // 模擬延時
emitter.send("實時消息:更新來了!");
emitter.complete(); // 結束推送
} catch (Exception e) {
emitter.completeWithError(e); // 異常處理
}
}).start();
return emitter;
}
}
客戶端代碼
const eventSource = new EventSource("/sse");
// 處理服務器推送的消息
eventSource.onmessage = function(event) {
console.log(event.data);
};
// 處理連接關閉或錯誤
eventSource.onerror = function() {
console.log("連接出現(xiàn)問題,自動重連...");
};
效果預覽
圖片
為什么會這么多,那是因為客戶端的自動重連機制,無需我們手動維護,客戶端會自動發(fā)起重連。
SseEmitter
其實sse的核心,就是SseEmitter這個類,是 Spring 提供的一個類,用于處理 Server-Sent Events (SSE)。它允許服務器端以流的形式推送事件給客戶端,而不需要客戶端不斷輪詢服務器。
1.構造函數(shù)
- SseEmitter():創(chuàng)建一個默認超時時間的 SseEmitter 實例。默認超時為 30 秒。
- SseEmitter(Long timeout):創(chuàng)建一個帶有自定義超時時間的 SseEmitter 實例。
timeout:指定以毫秒為單位的超時時間。如果設置為 0L,則連接永遠不會超時。
2.核心方法
- send(Object object):向客戶端發(fā)送一條消息。
object:要發(fā)送的數(shù)據(jù),可以是任何類型的對象。
此方法會將數(shù)據(jù)直接發(fā)送到客戶端,并在響應體中流式返回。
- send(SseEmitter.SseEventBuilder event):以事件構建器的形式發(fā)送一條消息。
SseEventBuilder 是用來構建發(fā)送事件的一個內部類,允許你自定義事件的各個屬性,如 id、data、name 等。
complete():表示 SSE 流完成并關閉連接。服務器告訴客戶端,事件流已經結束。
completeWithError(Throwable ex):在發(fā)生錯誤時關閉連接,并以錯誤的形式告知客戶端。
3.回調函數(shù)
- onCompletion(Runnable callback):指定當 SSE 連接完成(正常關閉)時執(zhí)行的回調函數(shù)。
- onTimeout(Runnable callback):指定當連接超時時執(zhí)行的回調函數(shù)。
- onError(Consumer<Throwable> callback):指定當發(fā)生錯誤時執(zhí)行的回調函數(shù)。這個錯誤可能是由于網絡連接問題、客戶端斷開等原因。
4.SseEventBuilder 內部類
SseEmitter.SseEventBuilder 是用于構建 SSE 事件的一個類,允許自定義事件的各個部分:
- SseEmitter.event():返回一個新的 SseEventBuilder 實例,用于構建事件。
- id(String id):設置事件的唯一標識符??蛻舳丝梢酝ㄟ^這個 ID 識別和處理事件。
- name(String name):設置事件的名稱??蛻舳丝梢酝ㄟ^這個名稱識別不同類型的事件,SSE 響應中會顯示為 event: name。
- data(Object data):設置要發(fā)送的數(shù)據(jù)??梢允俏谋尽SON 等類型,最終會在客戶端的 SSE 流中顯示為 data: xxx。
- reconnectTime(long milliseconds):告訴客戶端在多少毫秒后嘗試重新連接。如果連接中斷,客戶端會在指定時間后自動重連。
- comment(String comment):向客戶端發(fā)送一條注釋(不會觸發(fā)事件)。
目前 SseEmitter 是基于 每個客戶端請求獨立管理 的對象,因此不適合將其直接交由 Spring 管理為單例或共享對象。
每次請求應手動創(chuàng)建新的 SseEmitter 實例,并配置合適的超時時間。對于每個連接,SseEmitter 都是短暫的,使用完畢后應該調用 complete() 或 completeWithError() 方法來釋放資源。
SSE 與 WebSocket 對比
圖片
小結
- 相比輪詢,SSE 通過長連接減少了網絡開銷和服務器壓力。
- SseEmitter 適用于需要服務器實時推送數(shù)據(jù)的場景,特別是實時通知、動態(tài)更新等需求
- 相比websocket,在某些場景下,SSE 更加易用,且占用的資源較少