Spring Boot中使用SSE(Server-Sent Events)實現(xiàn)聊天功能:替代websocket服務器推送
一、概述
提到服務端數(shù)據(jù)推送,你可以一下子就想到了Websocket,WebSocket是一種全新的協(xié)議,隨著HTML5草案的不斷完善,越來越多的現(xiàn)代瀏覽器開始全面支持WebSocket技術了,它將TCP的Socket(套接字)應用在了webpage上,從而使通信雙方建立起一個保持在活動狀態(tài)連接通道。
但你可能不知道,HTML5中有一個輕量的替代Websocket的方案:SSE(Server-Sent Events)。
WebSocket 和 SSE 都是傳統(tǒng)請求-響應 Web 架構的替代方案,但它們不是完全沖突的技術。 - WebSocket 架構在客戶端與服務器之間打開一個套接字,用于實現(xiàn)全雙工(雙向)通信。 無需發(fā)送 GET 消息并等待服務器響應,客戶端只需監(jiān)聽該套接字,接收服務器更新,并使用收到的數(shù)據(jù)來發(fā)起或支持各種交互。 客戶端也可以使用套接字與服務器通信,例如在成功收到更新時發(fā)送 ACK 消息。 - SSE 是一種更簡單的標準,是作為 HTML5 的擴展而開發(fā)的。 盡管 SSE 支持從服務器向客戶端發(fā)送異步消息,但客戶端無法向服務器發(fā)送消息。 對于客戶端只需接收從服務器傳入的更新的應用程序,SSE 的半雙工通信模型最適合。 與 WebSocket 相比,SSE 的一個優(yōu)勢是它是基于 HTTP 而運行的,不需要其他組件。
幾乎所有現(xiàn)代瀏覽器都支持 WebSocket 協(xié)議,包括移動瀏覽器。然而Microsoft IE 和 Edge不支持SSE 但這并不妨礙我們使用SSE,畢竟用IE的人還有幾個呢?如果是內(nèi)部使用,為什么不使用更簡單的SSE呢?
這里講述如何使用SSE建立服務端的推送。
二、服務端
這里我們使用聊天來模擬SSE的數(shù)據(jù)推送。我這里寫了幾個自定義的對象 - Chater對象存儲聊天人的信息。 - WebSSEUser是一個存儲用戶名userName和Chater對象的map。
2.1 配置
在Springboot項目中使用SSE,是不需要額外引入依賴的,只需要把spring-boot-starter-web引入即可。也不需要額外的配置。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>2.2 服務端text/event-stream長連接
要使用SSE,首先需要定義一個維持SSE長連接的接口地址,就像websocket中定義websocket的端口地址一樣,但是SSE這里和普通的http沒有多大區(qū)別,只是響應頭是text/event-stream.
示例:
@GetMapping(value = "/subscribe", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter to(HttpServletRequest request) {
String userName = (String) request.getSession().getAttribute("userName");
// 超時時間設置為3分鐘
SseEmitter sseEmitter = new SseEmitter(180000L);
Chater chater = WebSSEUser.getChater(userName);
sseEmitter.onTimeout(() -> chater.setSseEmitter(null));
sseEmitter.onCompletion(() -> System.out.println("完成?。?!"));
chater.setSseEmitter(sseEmitter);
return sseEmitter;
}這里是Springboot應用中使用SSE,我定義了/subscribe接口: - produces指定了響應類型text/event-stream - userName從session中獲取,并獲取到聊天對象Chater。 - 這里調(diào)用時創(chuàng)建SseEmitter對象,設置超時時間3分鐘,onTimeout超時后清除SseEmitter對象,因為SSE可以超時重連,超時會再次調(diào)用這個接口,就會重新生成SseEmitter對象。 - onCompletion完成后邏輯自定義,但是不要清除SseEmitter對象,否則會一直重連。
SSE調(diào)用/subscribe接口接口以后,會一直使用一個請求,類似websocket。
2.3 服務端發(fā)送消息
上面的代碼只是保持了長連接,而且是單向的,只能是服務端給客戶端發(fā)消息。
單向的意思就是,客戶端不能通過SSE去發(fā)送消息,服務端可以通過SSE給客戶端發(fā)送消息。
但是我們還是可以使用SSE來完成聊天功能的,因為客戶端可以通過普通http請求去發(fā)送消息,到服務端以后再發(fā)送給其他客戶端。
示例:
@RequestMapping(value = "/send")
public ResultModel send(@RequestBody MessageDTO<String> messageDTO, HttpServletRequest request) {
logger.info("收到發(fā)往用戶[{}]的文本請求;", messageDTO.getTargetUserName());
Object userName = request.getSession().getAttribute("userName");
if (userName == null)
return ResultModel.error("無用戶");
messageDTO.setFromUserName((String) userName);
messageDTO.setMessageType(Type.TYPE_TEXT.getMessageType());
Chater chater = WebSSEUser.getChater(messageDTO.getTargetUserName());
try {
chater.getSseEmitter().send(messageDTO);
} catch (IOException e) {
e.printStackTrace();
}
return ResultModel.ok();
}這里,通過目標的userName獲取到Chater對象,然后Chater對象中保存有SseEmitter對象,SseEmitter對象可以直接發(fā)送消息到客戶端。
三、客戶端
前面講述了服務端維持SSE的方法。下面講述下客戶端如何操作。
3.1 SSE連接
調(diào)用服務端的/subscribe接口,維持長連接,請閱服務端消息。
var url = "/subscribe";
var es = new EventSource(url);
es.addEventListener("message", function(e){
decode(e);
},false);
不需要額外引入js,addEventListener中可以調(diào)用其他方法對消息解析并操作。
這里的decode(e);是對應服務端的chater.getSseEmitter().send(messageDTO);
3.2 普通http請求發(fā)送消息
普通的ajax請求即可,無需額外處理,調(diào)用服務端的/send接口即可。
四、控制臺截圖
如圖:

- 客戶端連接服務端的
/subscribe接口,這個連接會一直持續(xù)下去,圖上已經(jīng)持續(xù)了十幾秒。 - 客戶端連接服務端的
/send接口,發(fā)送消息給服務端,服務端轉(zhuǎn)發(fā)給其他客戶端。
到此這篇關于Spring Boot中使用SSE(Server-Sent Events)實現(xiàn)聊天功能:替代websocket服務器推送的文章就介紹到這了,更多相關SSE(Server-Sent Events)替代websocket內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- Python使用WebSocket和SSE實現(xiàn)HTTP服務器消息推送方式
- chatGPT前端流式輸出js實現(xiàn)三種方法—fetch、SSE、websocket
- Websocket的用法及常見應用場景
- SpringBoot實現(xiàn)SSE(Server-Sent?Events)的完整指南
- Springboot?實現(xiàn)Server-Sent?Events的項目實踐
- Spring Boot中使用Server-Sent Events (SSE) 實現(xiàn)實時數(shù)據(jù)推送教程
- C#實現(xiàn)SSE(Server-Sent Events)服務端和客戶端的示例代碼
相關文章
Mybatis-Plus的條件構造器QueryWrapper & UpdateWrapper示例詳解
Mybatis-Plus的條件構造器QueryWrapper和UpdateWrapper為開發(fā)者提供了強大、靈活的條件構建工具,能夠大大簡化數(shù)據(jù)庫操作的代碼,通過本文的介紹,讀者可以更加深入地理解這兩個條件構造器的使用方法,并在實際項目中靈活應用,感興趣的朋友跟隨小編一起看看吧2024-01-01
Spring?Cloud?使用?Resilience4j?實現(xiàn)服務熔斷的方法
服務熔斷是為了保護我們的服務,比如當某個服務出現(xiàn)問題的時候,控制打向它的流量,讓它有時間去恢復,或者限制一段時間只能有固定數(shù)量的請求打向這個服務,這篇文章主要介紹了Spring?Cloud?使用?Resilience4j?實現(xiàn)服務熔斷,需要的朋友可以參考下2022-12-12
javaweb學習總結(jié)——使用JDBC處理MySQL大數(shù)據(jù)
本篇文章主要介紹了JDBC處理MySQL大數(shù)據(jù),有時是需要用程序把大文本或二進制數(shù)據(jù)直接保存到數(shù)據(jù)庫中進行儲存的,非常具有實用價值,需要的朋友可以參考下。2016-11-11
徹底理解Spring注解@Autowired實現(xiàn)原理
這篇文章主要為大家詳細的介紹了Spring注解@Autowired實現(xiàn)的原理,縝密的邏輯分析,實踐應用示例操作說明,讓大家徹底的理解Spring注解@Autowired背后實現(xiàn)原理2022-03-03
SpringBoot整合Quartz實現(xiàn)動態(tài)配置的代碼示例
這篇文章將介紹如何把Quartz定時任務做成接口,實現(xiàn)以下功能的動態(tài)配置添加任務,修改任務,暫停任務,恢復任務,刪除任務,任務列表,任務詳情,文章通過代碼示例介紹的非常詳細,需要的朋友可以參考下2023-07-07

