Springboot快速集成sse服務端推流(最新整理)
1、前言
如果項目中有一個場景,假設對接ChatGPT或對接天氣類接口的時候,需要服務端主動往客戶端進行消息推送或推流。通常的做法有:
- 客戶端提供接收數(shù)據(jù)接口,服務端開啟定時輪詢,定時向客戶端發(fā)起http請求
- 客戶端提供定時輪詢服務,定時向服務端發(fā)起http請求接口
- 使用websocket實時通訊
那么今天再介紹另一種機制:SSE,也就是服務器發(fā)送事件機制。
2、什么是SSE
SSE(Server-Sent Events)是一種允許服務器向客戶端推送實時數(shù)據(jù)的技術,它建立在 HTTP 和簡單文本格式之上,提供了一種輕量級的服務器推送方式,通常也被稱為“事件流”(Event Stream)。他通過在客戶端和服務端之間建立一個長連接,并通過這條連接實現(xiàn)服務端和客戶端的消息實時推送。
2.1、技術原理
SSE是建立在HTTP協(xié)議之上的,所以原理比較簡單,也與HTTP原理類似:
1)建立連接:
客戶端通過普通的 HTTP 請求向服務器發(fā)起連接請求,類似于普通的 Web 請求。這個請求的關鍵在于使用了 text/event-stream 的 MIME 類型,告知服務器該請求是 SSE 請求。
httpCopy codeGET /sse/stream HTTP/1.1 Host: example.com Accept: text/event-stream
2)服務器處理請求:
服務器接收到 SSE 請求后,會在連接上保持打開狀態(tài),不會立即關閉。這是與普通的請求-響應模式的主要不同之處。服務器端通過這個持久連接向客戶端發(fā)送數(shù)據(jù)。
3)數(shù)據(jù)推送:
服務器端通過打開的連接,周期性地向客戶端發(fā)送消息。這些消息以文本的形式發(fā)送,并遵循一定的格式,通常以 data 字段表示消息內容。
httpCopy codeHTTP/1.1 200 OK Content-Type: text/event-stream data: This is a message\n\n
上述例子中,data 字段包含了實際的消息內容,兩個換行符(\n\n)表示消息的結束。
4)客戶端接收消息:
客戶端通過監(jiān)聽連接的 message 事件來接收服務器推送的消息。一旦接收到消息,客戶端可以采取相應的操作,例如更新界面內容。
javascriptCopy codeconst eventSource = new EventSource('/sse/stream');
eventSource.onmessage = function (event) {
console.log('Received message:', event.data);
// 處理消息,例如更新界面
};5)連接關閉:
當服務器端不再需要向客戶端推送消息時,或者發(fā)生錯誤時,服務器可以關閉連接??蛻舳艘部梢酝ㄟ^調用 eventSource.close() 來關閉連接。
2.2、SSE和WebSocket
提到SSE,那自然要提一下WebSocket了。WebSocket是一種HTML5提供的全雙工通信協(xié)議(指可以在同一時間內允許兩個設備之間進行雙向發(fā)送和接收數(shù)據(jù)的通信協(xié)議),基于TCP協(xié)議,并復用HTTP的握手通道(允許一次TCP連接中傳輸多個HTTP請求和相應),常用于瀏覽器與服務器之間的實時通信。
SSE和WebSocket盡管功能類似,都是用來實現(xiàn)服務器向客戶端實時推送數(shù)據(jù)的技術,但還是有一定區(qū)別:
2.2.1、SSE (Server-Sent Events)
- 簡單性:SSE 使用簡單的 HTTP 協(xié)議,通常建立在標準的 HTTP 或 HTTPS 連接之上。這使得它對于一些簡單的實時通知場景非常適用,特別是對于服務器向客戶端單向推送數(shù)據(jù)。
- 兼容性:SSE 在瀏覽器端具有較好的兼容性,因為它是基于標準的 HTTP 協(xié)議的。即使在一些不支持 WebSocket 的環(huán)境中,SSE 仍然可以被支持。
- 適用范圍:SSE 適用于服務器向客戶端單向推送通知,例如實時更新、事件通知等。但它僅支持從服務器到客戶端的單向通信,客戶端無法直接向服務器發(fā)送消息。
2.2.2、WebSocket
全雙工通信: WebSocket 提供了全雙工通信,允許客戶端和服務器之間進行雙向實時通信。這使得它適用于一些需要雙向數(shù)據(jù)交換的應用,比如在線聊天、實時協(xié)作等。
低延遲:WebSocket 的通信開銷相對較小,因為它使用單一的持久連接,而不像 SSE 需要不斷地創(chuàng)建新的連接。這可以降低通信的延遲。
適用范圍: WebSocket 適用于需要實時雙向通信的應用,特別是對于那些需要低延遲、高頻率消息交換的場景。
2.2.3、選擇 SSE 還是 WebSocket?
簡單通知場景:如果你只需要服務器向客戶端推送簡單的通知、事件更新等,而不需要客戶端與服務器進行雙向通信,那么 SSE 是一個簡單而有效的選擇。
雙向通信場景:如果你的應用需要實現(xiàn)實時雙向通信,例如在線聊天、協(xié)作編輯等,那么 WebSocket 是更合適的選擇。
兼容性考慮: 如果你的應用可能在一些不支持 WebSocket 的環(huán)境中運行,或者需要考慮到更廣泛的瀏覽器兼容性,那么 SSE 可能是一個更可行的選擇。
3、Springboot快速集成
3.1、添加依賴
Springboot項目中,sse不需要額外添加依賴,引用了web相關的springboot依賴即可:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>3.2、創(chuàng)建SSE控制器
這里簡單創(chuàng)建一個控制器類,用于處理SSE請求。在JAVA中通常使用SSEmitter來實現(xiàn)sse的消息推送。
package com.example.springbootsse.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
import java.util.Date;
@RestController
@RequestMapping("/sse")
public class SSEmitterController {
@GetMapping("/stream")
public SseEmitter stream() {
// 用于創(chuàng)建一個 SSE 連接對象
SseEmitter emitter = new SseEmitter();
// 在后臺線程中模擬實時數(shù)據(jù)
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
// emitter.send() 方法向客戶端發(fā)送消息
// 使用SseEmitter.event()創(chuàng)建一個事件對象,設置事件名稱和數(shù)據(jù)
emitter.send(SseEmitter.event().name("message").data("[" + new Date() + "] Data #" + i));
Thread.sleep(1000);
}
// 數(shù)據(jù)發(fā)送完成后,關閉連接
emitter.complete();
} catch (IOException | InterruptedException e) {
// 發(fā)生錯誤時,關閉連接并報錯
emitter.completeWithError(e);
}
}).start();
return emitter;
}
}查看執(zhí)行結果,可以看到每一秒服務端都會自動像客戶端推送messag消息:

我們來關注下SSEmitter這個類,SseEmitter 是 Spring Framework 中用于實現(xiàn) Server-Sent Events(SSE)的一個類。它允許服務器向客戶端推送數(shù)據(jù),通過建立一個持久連接,實現(xiàn)服務器向客戶端的實時單向通信。在 Spring 框架中,SseEmitter 類通常用于處理 SSE 請求,推送事件給客戶端。
3.2.1、SSEmitter創(chuàng)建實例
SSEmitter提供了兩個構造函數(shù)用于創(chuàng)建實例。在創(chuàng)建實例時,我們可以指定超時時間timeout,如果傳0或使用無參構造,則表示永不過期。連接超時是指在一段時間內沒有數(shù)據(jù)傳輸時,連接將被認為是超時的,并自動關閉。

3.2.2、SSEmitter API
除此以外,SSEmitter還提供了幾種API,如上面例子中使用到的:
- emitter.send() 方法向客戶端發(fā)送消息。
- SseEmitter.event() 創(chuàng)建一個事件對象,設置事件名稱和數(shù)據(jù)。
- emitter.complete() 表示數(shù)據(jù)發(fā)送完成后關閉連接。
- emitter.completeWithError(e) 在發(fā)生錯誤時關閉連接并報錯。
3.2.3、SSEmitter注冊回調
SseEmitter 可以通過注冊回調函數(shù)來處理服務器端發(fā)往客戶端的事件。當服務器端有新的數(shù)據(jù)需要推送給客戶端時,注冊的回調函數(shù)將會被調用。SSEmitter繼承了ResponseBodyEmitter,提供的一系列注冊回調函數(shù)有:

示例代碼:
package com.example.springbootsse.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
import java.util.Date;
@RestController
@RequestMapping("/sse")
public class SSEmitterController {
@GetMapping("/stream")
public SseEmitter stream() {
// 3S超時
SseEmitter emitter = new SseEmitter(10000L);
// 注冊回調函數(shù),處理服務器向客戶端推送的消息
emitter.onCompletion(() -> {
System.out.println("Connection completed");
// 在連接完成時執(zhí)行一些清理工作
});
emitter.onTimeout(() -> {
System.out.println("Connection timeout");
// 在連接超時時執(zhí)行一些處理
emitter.complete();
});
// 在后臺線程中模擬實時數(shù)據(jù)
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
emitter.send(SseEmitter.event().name("message").data("[" + new Date() + "] Data #" + i));
Thread.sleep(1000);
}
emitter.complete(); // 數(shù)據(jù)發(fā)送完成后,關閉連接
} catch (IOException | InterruptedException e) {
emitter.completeWithError(e); // 發(fā)生錯誤時,關閉連接并報錯
}
}).start();
return emitter;
}
}- onCompletion():在連接完成時候觸發(fā),可在連接完成時執(zhí)行一些清理工作
- onTimeout():當連接超時時觸發(fā)
- onError():當連接異常時觸發(fā)
- completeWithError(e):用于發(fā)生錯誤時,關閉連接并報錯
4、小結
其實SSE已經出來很久了,但是熟知他的人卻很少,大多數(shù)項目中還是直接使用了websocket技術。直到最近ChatGPT火了之后,很多項目需要對接GPT進行實時推流,才逐漸又被人提起。所以借此篇文章給自己掃盲一下。
到此這篇關于Springboot集成sse服務端推流的文章就介紹到這了,更多相關Springboot集成sse內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
淺析Spring Boot中的spring-boot-load模塊
spring-boot-loader模塊允許我們使用java -jar archive.jar運行包含嵌套依賴jar的jar或者war文件,它提供了三種類啟動器。下面通過本文給大家介紹spring-boot-load模塊的相關知識,感興趣的朋友一起看看吧2018-01-01
淺談String類型如何轉換為time類型存進數(shù)據(jù)庫
這篇文章主要介紹了String類型如何轉換為time類型存進數(shù)據(jù)庫,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03
mybatis-plus update更新操作的三種方式(小結)
本文主要介紹了mybatis-plus update更新操作的三種方式,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-10-10
SpringBoot使用Editor.md構建Markdown富文本編輯器示例
這篇文章主要介紹了SpringBoot使用Editor.md構建Markdown富文本編輯器示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-03-03
使用Java生成永不重復的數(shù)字的實現(xiàn)方案
在現(xiàn)代應用開發(fā)中,一個常見的需求是生成 永不重復的數(shù)字,無論是在訂單系統(tǒng)中生成唯一訂單號,還是分布式系統(tǒng)中生成唯一標識,生成不重復的數(shù)字或ID都是至關重要的,本期我們將關注 Java 生產永不重復的數(shù)字,通過多個角度剖析不同場景下的解決方案,需要的朋友可以參考下2025-06-06

