使用Spring SseEmitter實現(xiàn)服務端推送的示例代碼
SseEmitter 是 Spring MVC 4.2+ 引入的一個類,專門用于實現(xiàn) Server-Sent Events (SSE)。簡單來說,SSE 允許服務器在建立連接后,主動向客戶端推送數(shù)據(jù),而不需要客戶端反復輪詢。
相比于 WebSocket,SSE 更輕量(基于 HTTP 協(xié)議),且支持自動重連。它非常適合像實時通知、股價更新、大模型(LLM)流式輸出這類單向推送場景。
核心原理解析
在傳統(tǒng)的 HTTP 請求中,客戶端發(fā)送請求,服務器返回響應,然后連接關閉。而 SseEmitter 改變了這種模式:
- 保持連接:服務器返回的響應頭包含
Content-Type: text/event-stream。 - 分塊傳輸:連接保持打開狀態(tài),服務器可以多次調(diào)用
emitter.send()發(fā)送數(shù)據(jù)包。 - 結束生命周期:手動調(diào)用
complete()或因為超時/錯誤觸發(fā)onTimeout/onError。
前言
在實時性需求日益增長的今天,我們不一定非要動用“重型武器” WebSocket。如果你只需要服務器向客戶端推送消息(如:進度條更新、ChatCompletion 響應),Spring 提供的 SseEmitter 可能是你的最佳選擇。
一、 核心代碼實現(xiàn)
使用 SseEmitter 通常分為三步:創(chuàng)建連接、保存引用、推送消息。
1. 控制器層 (Controller)
這是建立連接的入口。
@RestController
@RequestMapping("/api/sse")
public class SseController {
// 用于存儲已連接的客戶端,實際生產(chǎn)中建議使用專門的服務類管理
private static final Map<String, SseEmitter> emitters = new ConcurrentHashMap<>();
@GetMapping(value = "/subscribe/{userId}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter subscribe(@PathVariable String userId) {
// 設置超時時間(單位毫秒),0 表示永不超時
SseEmitter emitter = new SseEmitter(60_000L);
emitters.put(userId, emitter);
// 注冊回調(diào)
emitter.onCompletion(() -> emitters.remove(userId));
emitter.onTimeout(() -> emitters.remove(userId));
emitter.onError((e) -> emitters.remove(userId));
// 建立連接后立即發(fā)送一條消息,防止某些代理或瀏覽器認為連接已失效
try {
emitter.send(SseEmitter.event().name("INIT").data("Connected!"));
} catch (IOException e) {
emitter.completeWithError(e);
}
return emitter;
}
}
2. 消息推送邏輯
你可以從任何異步線程調(diào)用 send 方法。
public void sendMessage(String userId, String content) {
SseEmitter emitter = emitters.get(userId);
if (emitter != null) {
try {
// 可以發(fā)送純文本,也可以發(fā)送封裝好的對象(會自動轉(zhuǎn) JSON)
emitter.send(SseEmitter.event()
.id(UUID.randomUUID().toString())
.name("message") // 對應前端的事件名
.data(content)
.reconnectTime(3000)); // 提示客戶端重連間隔
} catch (IOException e) {
emitters.remove(userId);
emitter.completeWithError(e);
}
}
}
二、 前端如何接收?
瀏覽器內(nèi)置了 EventSource API,調(diào)用非常簡單:
const eventSource = new EventSource('/api/sse/subscribe/user123');
eventSource.onmessage = (event) => {
console.log("收到通用消息:", event.data);
};
// 監(jiān)聽特定類型的事件 (對應后端的 .name("message"))
eventSource.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
console.log("處理后的數(shù)據(jù):", data);
});
eventSource.onerror = (error) => {
console.error("SSE 錯誤:", error);
eventSource.close();
};
三、 避坑指南(關鍵技巧)
- Nginx 配置:如果你使用了 Nginx 代理,必須配置
proxy_buffering off;和proxy_set_header Connection "";,否則 Nginx 會緩存消息直到緩沖區(qū)滿才一次性發(fā)給前端。 - 容器線程限制:雖然
SseEmitter是異步的,但它依然占用一個 HTTP 連接。在高并發(fā)場景下,需要調(diào)整 Web 容器(如 Tomcat)的最大連接數(shù)。 - 超時處理:瀏覽器默認會在 SSE 斷開后自動重連。在
onTimeout中,務必調(diào)用emitter.complete()來清理服務器資源。 - 跨域問題:確保 CORS 配置允許
Last-Event-ID等特殊請求頭。
總結
SseEmitter 是 Spring 為我們封裝的一把“輕巧的瑞士軍刀”。它規(guī)避了 WebSocket 復雜的握手和協(xié)議轉(zhuǎn)換,在處理流式輸出(如 AI 對話響應)時表現(xiàn)極佳。
以上就是使用Spring SseEmitter實現(xiàn)服務端推送的示例代碼的詳細內(nèi)容,更多關于Spring SseEmitter服務端推送的資料請關注腳本之家其它相關文章!
相關文章
IntelliJ?IDEA社區(qū)版2021.3配置SpringBoot項目詳細教程及失敗案例
IntelliJ?IDEA?2021.3.3是一款集成開發(fā)環(huán)境,用于Java和其他編程語言的開發(fā),下面這篇文章主要給大家介紹了關于IntelliJ?IDEA社區(qū)版2021.3配置SpringBoot項目詳細教程及失敗案例的相關資料,需要的朋友可以參考下2024-03-03
idea本地jar使用maven打包本地依賴實現(xiàn)自動編譯到項目里的操作
這篇文章主要介紹了idea本地jar使用maven打包本地依賴實現(xiàn)自動編譯到項目里的操作,本文通過示例代碼給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧2024-05-05
Java連接SQL?Server數(shù)據(jù)庫的超詳細教程
在Java應用程序中我們經(jīng)常需要與數(shù)據(jù)庫進行交互,一種常見的數(shù)據(jù)庫是Microsoft?SQL?Server,下面這篇文章主要給大家介紹了關于Java連接SQL?Server數(shù)據(jù)庫的超詳細教程,需要的朋友可以參考下2024-01-01
Java中的synchronized有幾種加鎖方式(實例詳解)
在Java中,synchronized關鍵字提供了內(nèi)置的支持來實現(xiàn)同步訪問共享資源,以避免并發(fā)問題,這篇文章主要介紹了java的synchronized有幾種加鎖方式,需要的朋友可以參考下2024-05-05

