極致流式推送!SpringBoot + ResponseBodyEmitter 讓異步更絲滑
隨著 ChatGPT 的興起,流式輸出技術(shù)逐漸走進(jìn)大眾視野。在技術(shù)社區(qū)中,很多開發(fā)者開始研究和實踐 SSE(服務(wù)器推送事件)。不過,SSE 仍然存在一定的局限性,而 Spring 提供的 ResponseBodyEmitter 則是一種更簡便的異步流式輸出方式。雖然 ResponseBodyEmitter 并非新技術(shù),它早在 Spring Framework 4.2 版本就已經(jīng)引入,但在實際開發(fā)中仍然具有極高的實用價值,特別是在處理實時數(shù)據(jù)推送時。
ResponseBodyEmitter 的作用
ResponseBodyEmitter 主要用于異步 HTTP 響應(yīng)處理,其核心優(yōu)勢在于 支持逐步向客戶端發(fā)送數(shù)據(jù),而不是一次性返回所有內(nèi)容。這種特性特別適用于流式傳輸或需要長時間處理的任務(wù),例如:
- 長輪詢服務(wù)器保持連接開放,待有數(shù)據(jù)時立即返回。
- 服務(wù)器推送事件(SSE)持續(xù)向客戶端推送更新信息。
- 流式傳輸逐步推送大量數(shù)據(jù),如文件下載或?qū)崟r數(shù)據(jù)流。
- 異步處理在后臺執(zhí)行耗時任務(wù),并實時返回處理結(jié)果。
典型應(yīng)用場景
在實際業(yè)務(wù)中,ResponseBodyEmitter 可廣泛應(yīng)用于:
- 實時進(jìn)度更新(例如文件上傳進(jìn)度條)
- 實時聊天
- 股票價格推送
- AI 實時響應(yīng)
- 服務(wù)器日志流式輸出
實戰(zhàn):構(gòu)建實時日志流
假設(shè)我們需要實現(xiàn)一個實時日志查看功能,服務(wù)器可以不斷地向客戶端推送最新的日志信息。
創(chuàng)建控制器
首先,在 Spring Boot 3.4 應(yīng)用中創(chuàng)建 LogController,使用 ResponseBodyEmitter 處理流式日志輸出。
package com.icoderoad.controller;
importorg.springframework.http.MediaType;
importorg.springframework.web.bind.annotation.GetMapping;
importorg.springframework.web.bind.annotation.RequestMapping;
importorg.springframework.web.bind.annotation.RestController;
importorg.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;
@RestController
@RequestMapping("/api/log")
publicclassLogController{
@GetMapping(value ="/stream", produces =MediaType.TEXT_EVENT_STREAM_VALUE)
publicResponseBodyEmitterstreamLogs(){
ResponseBodyEmitter emitter =newResponseBodyEmitter();
newThread(()->{
try{
while(true){
String logEntry =getLatestLogEntry();
if(logEntry !=null){
emitter.send(logEntry);
}
Thread.sleep(1000);// 每秒推送一次
}
}catch(Exception e){
emitter.completeWithError(e);
}
}).start();
return emitter;
}
privateStringgetLatestLogEntry(){
return"2025-02-12 12:00:00 - INFO: 用戶成功登錄。";
}
}
運行效果:
訪問 http://localhost:8080/api/log/stream,服務(wù)器將每秒推送一條新的日志條目,前端可以實時接收并展示。
ResponseBodyEmitter 的核心方法
- send(Object data)逐步向客戶端發(fā)送數(shù)據(jù),可多次調(diào)用。
- complete()結(jié)束響應(yīng)流。
- onTimeout(Runnable callback)連接超時時觸發(fā)回調(diào)。
- onCompletion(Runnable callback)數(shù)據(jù)發(fā)送完成后觸發(fā)回調(diào)。
ResponseBodyEmitter 的工作原理
異步數(shù)據(jù)生成與推送
在傳統(tǒng) HTTP 響應(yīng)模式中,服務(wù)器通常需要等待整個響應(yīng)數(shù)據(jù)生成完成后再返回。而 ResponseBodyEmitter 允許 異步逐步推送數(shù)據(jù),即部分?jǐn)?shù)據(jù)準(zhǔn)備好后立即 send() 給客戶端,提高響應(yīng)速度。
分塊傳輸(Chunked Encoding)
傳統(tǒng) HTTP 響應(yīng)頭需要指定 Content-Length,但 ResponseBodyEmitter 采用 分塊傳輸(Chunked Encoding),無需指定數(shù)據(jù)總長度,而是將數(shù)據(jù)分塊推送,客戶端收到后即可立即處理,減少等待時間。
連接生命周期管理
- 數(shù)據(jù)發(fā)送完畢時調(diào)用 complete() 關(guān)閉連接,避免資源浪費。
- 若發(fā)生異常,調(diào)用 completeWithError(),通知客戶端連接關(guān)閉。
注意事項
- 客戶端支持:大多數(shù)現(xiàn)代瀏覽器和 HTTP 客戶端都支持 ResponseBodyEmitter,但部分舊版本可能存在兼容性問題。
- 超時管理:建議設(shè)置超時時間,避免長連接占用資源,例如:
emitter.onTimeout(() -> emitter.complete());
- 線程安全:send() 方法是線程安全的,但仍需合理管理任務(wù)線程,避免資源泄漏。
- 連接關(guān)閉:務(wù)必調(diào)用 complete() 或 completeWithError() 以釋放資源。
ResponseBodyEmitter 與其他流式技術(shù)對比
技術(shù)方案 | 適用場景 | 優(yōu)缺點 |
Streaming | 低級字節(jié)流傳輸 | 靈活性高,但需要手動管理 |
SSE | 服務(wù)器推送事件 | 需瀏覽器支持 |
ResponseBodyEmitter | 通用 HTTP 流式傳輸 | 兼容性更好,易于與 Spring 集成 |
在 AI 流式輸出等場景下,ResponseBodyEmitter 相比 SSE 具有更好的 HTTP 兼容性,且使用方式更靈活。
結(jié)語
ResponseBodyEmitter 是 Spring 提供的一種輕量級流式傳輸解決方案,非常適用于 高并發(fā)、實時數(shù)據(jù)推送 需求。無論是 進(jìn)度條實時更新、實時聊天、股票數(shù)據(jù)推送、系統(tǒng)日志流式輸出,它都能提供更絲滑的用戶體驗。如果你的應(yīng)用需要高效的異步數(shù)據(jù)推送,不妨試試 ResponseBodyEmitter 吧!