GPT 的流式交互,Spring 可以實現嗎?
先看一張和 GPT交互的圖片,讓 GPT 寫一篇200字的詩歌贊美 Java:
那么問題來了,我們是否也可以輕松地實現這種流式交互?
但是是必須的,這篇文章我們就來聊一聊主角:Spring SseEmitter。
1. 什么是 SSE?
SseEmitter 是 Spring MVC 中用于實現服務器發送事件(Server-Sent Events, 簡稱 SSE)的一個類,它是一種基于 HTTP 協議的標準,用于服務器向客戶端單向推送事件。
SSE 允許服務器通過單向通道向客戶端持續推送數據,適用于需要實時更新的應用場景,如實時通知、消息推送、動態數據展示等。SseEmitter 的工作原理主要涉及以下幾個方面:
SSE 的特點:
- 單向通信:僅服務器可以主動發送數據到客戶端。
- 持久連接:使用持久的 HTTP 連接,服務器可以持續發送事件。
- 自動重連:瀏覽器在連接斷開后會自動嘗試重連。
- 基于文本:傳輸的數據格式為純文本,通常為 UTF-8 編碼。
2. SseEmitter如何實現?
使用 SseEmitter 實現像 GPT一樣的流式交互,其實還是比較簡單的,在控制器中創建 SseEmitter 并返回,示例代碼如下:
@RestController
publicclass SseController {
@GetMapping("/sse")
public SseEmitter streamSseMvc() {
SseEmitter emitter = new SseEmitter();
// 異步處理發送事件
Executors.newSingleThreadExecutor().execute(() -> {
try {
for (int i = 0; i < 10; i++) {
emitter.send("Message " + i);
Thread.sleep(1000);
}
emitter.complete();
} catch (Exception e) {
emitter.completeWithError(e);
}
});
return emitter;
}
}
主要方法:
- send(Object object):發送事件數據給客戶端。
- complete():關閉連接。
- completeWithError(Throwable ex):在發生錯誤時關閉連接并發送錯誤信息。
效果如下圖:
3. 工作流程
(1) 客戶端請求 SSE 端點
客戶端通過 EventSource API 或其他方式向服務器的 SSE 端點發送 HTTP GET 請求。例如:
const eventSource = new EventSource('/sse');
eventSource.onmessage = function(event) {
console.log('Received event:', event.data);
};
eventSource.onerror = function(err) {
console.error('EventSource failed:', err);
};
(2) 服務器端建立 SseEmitter
當服務器接收到 SSE 請求時,控制器方法會創建一個 SseEmitter 實例并返回。這會觸發 Spring MVC 將響應頭設置為 Content-Type: text/event-stream,以維持持久連接。
(3) 服務器端發送事件
通過 SseEmitter.send() 方法,服務器可以向客戶端發送事件數據。通常,這些操作會在異步線程中進行,以避免阻塞主線程。
(4) 持久連接和生命周期管理
SseEmitter 管理著 SSE 連接的生命周期,包括處理超時、連接斷開和錯誤等情況。可以通過配置超時時間來控制連接的最長持續時間:
SseEmitter emitter = new SseEmitter(30_000L); // 30秒超時
如果連接在指定時間內未關閉,SseEmitter 會自動觸發超時處理。
(5) 客戶端接收事件
客戶端通過 EventSource 接收并處理服務器發送的事件。當服務器調用 emitter.complete() 或連接因超時等原因關閉時,客戶端的 onclose 事件會被觸發。
4. 錯誤處理與重試機制
- 服務器端:在發送事件過程中,如果發生異常,可以調用 emitter.completeWithError(e) 來通知客戶端錯誤并關閉連接。
- 客戶端端:客戶端的 EventSource 會自動嘗試重新連接,當連接斷開時,會觸發 onerror 事件??梢栽诳蛻舳舜a中實現更復雜的重試邏輯,例如增加重試次數限制或延遲策略。
5. 適用場景與限制
(1) 適用場景
- 實時通知,如聊天應用、社交媒體動態更新。
- 實時監控,如服務器狀態監控、數據儀表盤。
- 需要頻繁推送更新但數據量不大的場景。
(2) 限制
- 僅支持服務器到客戶端的單向通信。
- 需要瀏覽器支持 SSE 協議(大多數現代瀏覽器支持,但部分老舊瀏覽器可能不兼容)。
- 對于需要高頻率、大數據量的實時通信,WebSocket 可能更為合適。
6. 總結
這篇文章,我們分析了如何使用SseEmitter實現客戶端和服務器的流式交互,SseEmitter提供了一個簡潔的方式在 Spring 應用中實現服務器發送事件,通過維護持久連接和異步事件推送,滿足了大多數實時數據推送的需求。