Spring Boot 整合 SSE, http長連接

1. 什么是 SSE? (30秒)

SSE (Server-Sent Events) 是一種允許服務器通過 HTTP 連接主動向客戶端發送實時更新的技術。

  • 特點:基于 HTTP,使用簡單,單向通信(服務器 -> 客戶端),自動重連。

  • 對比 WebSocket:WebSocket 是雙向的,更復雜;SSE 是單向的,更輕量,適用于通知、日志流、實時數據更新等場景。


2. 核心依賴與配置 (30秒)

Spring Boot 從 2.2.x 版本開始提供了對 SSE 的專用支持,主要包含在?spring-boot-starter-web?中,無需引入額外依賴

確保你的?pom.xml?中有:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>

3. 三步編寫代碼 (3分鐘)

第一步:創建控制器 (Controller)

創建一個?@RestController,并定義一個方法來產生 SSE 流。

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;@RestController
public class SseController {// 用于保存所有連接的 SseEmitter,可以根據用戶ID等關鍵字進行存儲private static final Map<String, SseEmitter> EMITTER_MAP = new ConcurrentHashMap<>();/*** 用于客戶端連接 SSE* @param clientId 客戶端標識,用于區分不同客戶端* @return SseEmitter*/@GetMapping(path = "/sse/connect", produces = MediaType.TEXT_EVENT_STREAM_VALUE)public SseEmitter connect(@RequestParam String clientId) {// 設置超時時間,0表示永不超時。可以根據需要設置,例如 30_000L (30秒)SseEmitter emitter = new SseEmitter(0L);// 注冊回調函數,當連接完成或出錯時,從Map中移除這個Emitteremitter.onCompletion(() -> EMITTER_MAP.remove(clientId));emitter.onError((e) -> EMITTER_MAP.remove(clientId));emitter.onTimeout(() -> EMITTER_MAP.remove(clientId));// 將新的 emitter 存入 MapEMITTER_MAP.put(clientId, emitter);// 可選:發送一個初始連接成功的事件try {emitter.send(SseEmitter.event().name("INIT") // 事件名稱,可選.data("連接成功 for: " + clientId) // 事件數據.id("1") // 事件ID,可選,用于重連.reconnectTime(5000)); // 重連時間,可選} catch (IOException e) {e.printStackTrace();}return emitter;}
}
第二步:創建發送消息的方法

在同一個 Controller 中,添加一個 API 來模擬向特定客戶端發送消息。

    /*** 向指定客戶端發送消息*/@GetMapping("/sse/send")public String sendMessage(@RequestParam String clientId, @RequestParam String message) {SseEmitter emitter = EMITTER_MAP.get(clientId);if (emitter != null) {try {// 構建并發送事件emitter.send(SseEmitter.event().name("MESSAGE") // 事件類型.data(message)   // 事件數據.id("msg-id-" + System.currentTimeMillis())); // ID} catch (IOException e) {// 發送失敗,移除 emitterEMITTER_MAP.remove(clientId);return "發送失敗,客戶端可能已斷開";}return "發送成功 to: " + clientId;}return "客戶端不存在";}
第三步:編寫前端頁面進行測試 (1分鐘)

在?src/main/resources/static?目錄下創建一個?sse-demo.html?文件。

<!DOCTYPE html>
<html>
<head><title>SSE Demo</title>
</head>
<body><h1>SSE 客戶端測試</h1><label for="clientId">客戶端ID: </label><input type="text" id="clientId" value="test-client-1"><button onclick="connectSSE()">連接SSE</button><button onclick="closeSSE()">斷開連接</button><hr><label for="message">要發送的消息: </label><input type="text" id="message" value="Hello SSE!"><button onclick="sendMessage()">發送消息</button><hr><h3>收到的事件:</h3><div id="messages"></div><script>let eventSource;function connectSSE() {const clientId = document.getElementById('clientId').value;// 斷開現有連接if (eventSource) {eventSource.close();}// 建立新的 SSE 連接eventSource = new EventSource(`/sse/connect?clientId=${clientId}`);// 監聽通用消息(沒有指定 event name 的消息)eventSource.onmessage = function (event) {appendMessage(`[message]: ${event.data}`);};// 監聽特定名稱的事件 (例如:MESSAGE)eventSource.addEventListener("MESSAGE", function (event) {appendMessage(`[MESSAGE]: ${event.data}`);});// 監聽特定名稱的事件 (例如:INIT)eventSource.addEventListener("INIT", function (event) {appendMessage(`[INIT]: ${event.data}`);});eventSource.onerror = function (err) {console.error("SSE error:", err);appendMessage('[錯誤] 連接出錯');};}function closeSSE() {if (eventSource) {eventSource.close();appendMessage('[信息] 連接已關閉');eventSource = null;}}function sendMessage() {const clientId = document.getElementById('clientId').value;const message = document.getElementById('message').value;fetch(`/sse/send?clientId=${clientId}&message=${encodeURIComponent(message)}`).then(response => response.text()).then(data => console.log(data));}function appendMessage(text) {const messageDiv = document.getElementById('messages');const p = document.createElement('p');p.textContent = `${new Date().toLocaleTimeString()}: ${text}`;messageDiv.appendChild(p);}</script>
</body>
</html>

4. 運行與測試 (1分鐘)

  1. 啟動應用:運行你的 Spring Boot 應用。

  2. 打開頁面:訪問?http://localhost:8080/sse-demo.html

  3. 進行測試

    • 輸入一個客戶端 ID(如?user1),點擊?“連接SSE”。前端會收到?[INIT]?事件。

    • 在另一個瀏覽器標簽頁或使用 Postman 訪問 :??http://localhost:8080/sse/send?clientId=user1&message=你好!

    • 觀察第一個標簽頁,會立即收到?[MESSAGE]: 你好!?的消息。

總結

  • 核心對象SseEmitter

  • 關鍵注解@GetMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE)

  • 流程

    1. 客戶端連接?/sse/connect,服務端創建并保存?SseEmitter

    2. 服務端通過?emitter.send()?主動推送消息。

    3. 客戶端通過?EventSource?API 監聽和處理消息。

    4. 連接結束時,服務端需要清理?SseEmitter(通過回調函數)。

現在你已經掌握了 Spring Boot 整合 SSE 的基本方法!在實際項目中,你可能需要將其與業務邏輯、身份認證(如 JWT)以及更強大的連接管理(如使用數據庫或 Redis 存儲 emitter)相結合。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/95057.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/95057.shtml
英文地址,請注明出處:http://en.pswp.cn/web/95057.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

類和反射的機制

一、類1.類的生命周期1. 類的編譯&#xff1a;通過 javac 命令將 .java 源文件編譯成 .class 字節碼文件。 2. 類的加載&#xff1a;類加載器&#xff08;ClassLoader&#xff09;將 .class 文件從硬盤加載到內存&#xff0c;形成“類對象”&#xff0c;包括加載、鏈接、初始化…

【論文筆記】VGGT-從2D感知3D:pose估計+稠密重建+點跟蹤

VGG組聯合Meta改進了dust3r&#xff0c;輸入圖片&#xff0c;輸出對應的一系列3D屬性&#xff0c;被CVPR2025收錄&#xff01;1.abstract我們提出了VGGT&#xff0c;一種前饋神經網絡&#xff0c;能夠直接從場景的一個、幾個或數百個視角推斷出所有關鍵的3D屬性&#xff0c;包括…

idea2025.2中maven編譯中文亂碼

問題描述&#xff1a;使用idea2025.2編譯器中maven編譯java文件后中文出現亂碼情況解決方案&#xff1a;添加指令&#xff1a; JAVA_TOOL_OPTIONS-Dfile.encodingUTF-8 在下圖位置注意&#xff1a;再次編譯時&#xff0c;可以在原本文件中小范圍修改一點內容&#xff08;打個…

【適度精簡】Windows 7 旗艦版-emmy精簡系統

Windows 7旗艦版曾是非常受歡迎的操作系統&#xff0c;但隨著時間推移和技術發展&#xff0c;其在一些場景下暴露出了諸多問題&#xff0c;適度精簡的Windows 7旗艦版正是為解決這些問題而出現&#xff0c;以下是從用戶軟件痛點角度對其背景和作用的分析。 添加圖片注釋&#x…

數據分析編程第七步:分析與預測

7.1 銷售趨勢分析利用歷史銷售數據統計月銷售額&#xff0c;計算季節化因子&#xff0c;獲取去季節化銷售數據&#xff0c;然后進行線性擬合&#xff0c;最后預測接下來的某個月的銷售額。第一步&#xff1a;讀數&#xff0c;統計月銷售額A1file(“sales.csv”).importtc(order…

【web3】十分鐘了解web3是什么?

十分鐘了解web3是什么?Web3的核心概念區塊鏈與去中心化智能合約加密貨幣與代幣去中心化應用&#xff08;DApps&#xff09;錢包與身份驗證DAO&#xff08;去中心化自治組織&#xff09;Web3 國內產品Web3 國際產品Web3 基礎設施Web3 應用場景技術實現特點挑戰與未來Web3的核心…

聯合體和枚舉——嵌入式學習筆記

目錄 前言 一、聯合體&#xff08;共用體&#xff09; 1、基本概念 2、初始化和引用 &#xff08;1&#xff09;初始化 &#xff08;2&#xff09;引用 二、枚舉 前言 在C語言的編程世界中&#xff0c;我們早已熟悉了結構體struct這種能將不同數據類型捆綁在一起的“打包…

SRE命令行兵器譜之思想篇:像SRE一樣思考——命令行不只是工具,更是你的戰友

SRE命令行兵器譜之思想篇:像SRE一樣思考——命令行不只是工具,更是你的戰友 歡迎來到《SRE命令行兵器譜》系列。在深入研究 grep, lsof, tcpdump 這些強大“兵器”的細節之前,我們必須先回答一個更重要的問題: 一個SRE(網站可靠性工程師)在黑色的終端窗口前,腦子里想的…

STL庫——list(類模擬實現)

? ? ? ? ? づ?ど &#x1f389; 歡迎點贊支持&#x1f389; 個人主頁&#xff1a;勵志不掉頭發的內向程序員&#xff1b; 專欄主頁&#xff1a;C語言&#xff1b; 文章目錄 前言 一、基本框架 二、構造函數 三、析構函數 四、賦值重載 五、增刪查改 5.1、push_front/pus…

在PowerPoint和WPS演示讓蝴蝶一直跳8字舞

如何讓PPT中插入的對象按指定的軌跡運動并且一直“停不下來”&#xff1f;簡單三步&#xff1a;①插入對象、②設置路徑動畫、③設置動畫重復。本文以蝴蝶圖片一直跳8字舞為例進行實際操作講解&#xff0c;PowerPoint和WPS演示都一樣操作&#xff0c;本文以WPS演示進行講解。第…

并發編程——06 JUC并發同步工具類的應用實戰

0 常用并發同步工具類的真實應用場景JDK 提供了比synchronized更加高級的各種同步工具&#xff0c;包括ReentrantLock、Semaphore、CountDownLatch、CyclicBarrier等&#xff0c;可以實現更加豐富的多線程操作&#xff1b;1 ReentrantLock&#xff08;可重入的占用鎖&#xff0…

Apple登錄接入記錄

Apple文檔——通過 Apple 登錄 使用入門 - 通過 Apple 登錄 - Apple Developer Apple文檔——設計要求——登錄通過 Apple 登錄 | Apple Developer Documentation 插件github版——apple-signin-unity&#xff08;README 中為接入步驟&#xff09; GitHub - lupidan/apple-…

【小程序-慕尚花坊04】網絡請求并發與loading

網絡請求并發與loading一&#xff0c;網絡請求并發與loading1&#xff0c;并發處理1.1&#xff0c;異步實現方式2.2&#xff0c;Promise.all異步方式封裝2&#xff0c;loading加載2.1&#xff0c;loading的基本使用2.2&#xff0c;loading與并發結合案例2.3&#xff0c;loading…

CentOS 7 升級 OpenSSH 10.0p2 完整教程(含 Telnet 備份)

&#x1f539; CentOS 7 升級 OpenSSH 10.0p2 完整教程&#xff08;含 Telnet 備份&#xff09; 注意&#xff1a;為了避免升級 SSH 時無法遠程登錄&#xff0c;建議先啟用 Telnet 服務 作為備用連接方式。 CentOS 7 默認 OpenSSH 版本是 7.x&#xff0c;升級到 10.0p2 需要 源…

aragfw9.dll aqnky-ef.dll aqua dock.dll apscon~1.dll apropdll.dll app_web_yqnqasrp.dll app_web_

在使用電腦系統時經常會出現丟失找不到某些文件的情況&#xff0c;由于很多常用軟件都是采用 Microsoft Visual Studio 編寫的&#xff0c;所以這類軟件的運行需要依賴微軟Visual C運行庫&#xff0c;比如像 QQ、迅雷、Adobe 軟件等等&#xff0c;如果沒有安裝VC運行庫或者安裝…

rabbitMQ延時隊列實現,怎么保證消息的冪等

一、RabbitMQ 延時隊列實現方式 基于 TTL&#xff08;Time-To-Live&#xff09; 死信隊列&#xff08;Dead Letter Queue&#xff09; 這是最常用的實現方式&#xff0c;核心思路是&#xff1a; (1)消息設置過期時間&#xff08;TTL&#xff09; (2)消息過期后進入綁定的死信隊…

前沿技術觀察:從AI 時代到量子計算的下一站

前沿技術觀察&#xff1a;從AI 時代到量子計算的下一站&#x1f680; 技術的浪潮一波接一波&#xff0c;從 人工智能 到 區塊鏈&#xff0c;再到 邊緣計算、元宇宙、量子計算&#xff0c;這些前沿技術正在深刻影響我們的生活與產業格局。 對于開發者和技術愛好者來說&#xff0…

通過Kubernetes安裝mysql5服務

以下是清晰、結構化的操作流程優化說明&#xff0c;按步驟梳理從部署到配置持久化、暴露服務的完整過程&#xff1a;一、基礎部署&#xff1a;快速驗證 MySQL 可用性創建有狀態工作負載進入 KubeSphere 項目 → 工作負載 → 有狀態副本集 → 創建&#xff0c;選擇 通過鏡像創建…

【mysql】SQL 中 IS 與 = 的區別:一個 NULL 值引發的思考

SQL 中 IS 與 的區別&#xff1a;一個 NULL 值引發的思考為什么查詢結果總是少一條數據&#xff1f;可能是 NULL 在搗鬼在 SQL 查詢中&#xff0c;很多開發者都曾遇到過這樣的困惑&#xff1a;明明看起來正確的查詢語句&#xff0c;返回的結果卻總是與預期不符。這往往是因為沒…

openGauss筆記

1、安裝 直接用docker安裝 2、國產化 符合國產化要求 3、客戶端 3.1 dbeaver 社區版本&#xff08;25.1.4&#xff09;即可&#xff0c;驅動建議用離線版本&#xff0c;在官網下載最新的&#xff0c;然后在驅動管理里面進行添加本地的jar 3.1.1 驅動配置3.1.2 依賴 需要java版本…