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






































