SpringBoot環(huán)境下服務(wù)端向客戶端主動(dòng)推送數(shù)據(jù)的幾種常見方式
前言
在傳統(tǒng)的 HTTP 請(qǐng)求-響應(yīng)模型中,客戶端需要主動(dòng)發(fā)起請(qǐng)求,服務(wù)端才能返回?cái)?shù)據(jù)。然而,在某些場(chǎng)景下(如實(shí)時(shí)聊天、股票行情更新、通知系統(tǒng)等),我們希望服務(wù)端能夠主動(dòng)向客戶端推送數(shù)據(jù)。本文將詳細(xì)介紹在 Spring Boot 環(huán)境下實(shí)現(xiàn)服務(wù)端向客戶端主動(dòng)推送數(shù)據(jù)的幾種常見方式,并比較它們的優(yōu)缺點(diǎn)和適用場(chǎng)景。
一、什么是“服務(wù)端推送”?
“服務(wù)端推送”是指服務(wù)端在沒有收到客戶端請(qǐng)求的情況下,主動(dòng)將數(shù)據(jù)發(fā)送給客戶端。這種機(jī)制打破了傳統(tǒng) HTTP 的單向通信限制,適用于需要實(shí)時(shí)交互的場(chǎng)景。
二、常用推送技術(shù)概述
以下是目前主流的幾種服務(wù)端向客戶端推送數(shù)據(jù)的方式:
- 長(zhǎng)輪詢(Long Polling)
- Server-Sent Events(SSE)
- WebSocket
- MQTT(物聯(lián)網(wǎng)常用)
- 基于消息中間件 + 客戶端監(jiān)聽(如 RabbitMQ + STOMP)
本文主要聚焦于前三種與 Spring Boot 集成較為方便的技術(shù)。
三、方法一:長(zhǎng)輪詢(Long Polling)
1. 原理簡(jiǎn)介
客戶端定時(shí)或持續(xù)發(fā)起請(qǐng)求到服務(wù)端,服務(wù)端如果無新數(shù)據(jù)則保持連接不返回,直到有數(shù)據(jù)或超時(shí)后才響應(yīng)??蛻舳耸盏巾憫?yīng)后立即發(fā)起下一次請(qǐng)求。
2. 優(yōu)點(diǎn)
- 兼容性好,支持所有瀏覽器
- 實(shí)現(xiàn)簡(jiǎn)單,適合小型項(xiàng)目或低頻更新
3. 缺點(diǎn)
- 連接頻繁建立銷毀,資源消耗大
- 推送延遲較高
4. Spring Boot 示例代碼
Controller 層
@RestController
public class LongPollingController {
private String latestData = "No new data";
@GetMapping("/poll")
public String poll(@RequestParam String clientId) throws InterruptedException {
synchronized (this) {
wait(10000); // 模擬等待新數(shù)據(jù)
}
return latestData;
}
@PostMapping("/update")
public void updateData(@RequestBody Map<String, String> payload) {
this.latestData = payload.get("data");
synchronized (this) {
notifyAll(); // 通知所有等待線程
}
}
}
客戶端模擬(JavaScript)
function startPolling() {
fetch("/poll?clientId=1")
.then((res) => res.text())
.then((data) => {
console.log("Received:", data)
startPolling() // 繼續(xù)下一次輪詢
})
}
startPolling()
四、方法二:Server-Sent Events(SSE)
1. 原理簡(jiǎn)介
SSE 是 HTML5 提供的一種服務(wù)器向客戶端推送事件的標(biāo)準(zhǔn)協(xié)議。它是單向通信,即服務(wù)端可以不斷向客戶端發(fā)送數(shù)據(jù),但客戶端不能通過該通道向服務(wù)端發(fā)送數(shù)據(jù)。
2. 優(yōu)點(diǎn)
- 實(shí)時(shí)性強(qiáng)
- 協(xié)議輕量,易于實(shí)現(xiàn)
- 支持自動(dòng)重連
3. 缺點(diǎn)
- 只能服務(wù)端推送送,不支持雙向通信
- 不兼容 IE 瀏覽器
4. Spring Boot 示例代碼
Controller 層
@RestController
public class SseController {
private final List<SseEmitter> emitters = new CopyOnWriteArrayList<>();
@GetMapping("/subscribe")
public SseEmitter subscribe() {
SseEmitter emitter = new SseEmitter(60_000L); // 超時(shí)時(shí)間
emitters.add(emitter);
emitter.onCompletion(() -> emitters.remove(emitter));
return emitter;
}
@PostMapping("/send")
public void sendData(@RequestBody Map<String, String> payload) {
String message = payload.get("message");
emitters.forEach(emitter -> {
try {
emitter.send(message);
} catch (IOException e) {
emitter.complete();
emitters.remove(emitter);
}
});
}
}
客戶端代碼(HTML + JavaScript)
<script>
const eventSource = new EventSource("/subscribe")
eventSource.onmessage = function (event) {
console.log("New message:", event.data)
}
eventSource.onerror = function (err) {
console.error("EventSource failed:", err)
}
</script>
五、方法三:WebSocket
1. 原理簡(jiǎn)介
WebSocket 是一種全雙工通信協(xié)議,允許服務(wù)端和客戶端之間建立持久連接并隨時(shí)互相發(fā)送數(shù)據(jù)。是目前最強(qiáng)大的實(shí)時(shí)通信解決方案之一。
2. 優(yōu)點(diǎn)
- 實(shí)時(shí)性強(qiáng),延遲低
- 支持雙向通信
- 數(shù)據(jù)傳輸效率高
3. 缺點(diǎn)
- 實(shí)現(xiàn)相對(duì)復(fù)雜
- 需要客戶端和服務(wù)端都支持 WebSocket
- 部分防火墻或代理可能不支持
4. Spring Boot 示例代碼
1. 添加依賴(pom.xml)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2. 配置 WebSocket
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic");
registry.setApplicationDestinationPrefixes("/app");
}
}
3. 發(fā)送消息的 Controller
@Controller
public class WsController {
@Autowired
private SimpMessagingTemplate messagingTemplate;
@PostMapping("/broadcast")
public void broadcast(@RequestBody Map<String, String> payload) {
String message = payload.get("message");
messagingTemplate.convertAndSend("/topic/messages", message);
}
}
4. 客戶端代碼(使用 SockJS + Stomp.js)
<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/stompjs/lib/stomp.min.js"></script>
<script>
const socket = new SockJS("/ws")
const stompClient = Stomp.over(socket)
stompClient.connect({}, () => {
stompClient.subscribe("/topic/messages", (msg) => {
console.log("Received:", msg.body)
})
})
</script>
六、其他方式簡(jiǎn)要介紹
1. MQTT(適用于物聯(lián)網(wǎng)場(chǎng)景)
- 輕量級(jí)發(fā)布/訂閱協(xié)議
- 適用于設(shè)備間通信
- 可以結(jié)合 Spring Boot + EMQX / Mosquitto 使用
2. 消息隊(duì)列 + 客戶端監(jiān)聽(如 RabbitMQ)
- 服務(wù)端將數(shù)據(jù)發(fā)到 MQ
- 客戶端監(jiān)聽某個(gè)隊(duì)列或 Exchange
- 適合解耦和分布式系統(tǒng)架構(gòu)
七、對(duì)比總結(jié)表
| 方法 | 通信方向 | 是否實(shí)時(shí) | 是否雙向 | 易用性 | 適用場(chǎng)景 |
|---|---|---|---|---|---|
| 長(zhǎng)輪詢 | 單向 | 否(延遲高) | 否 | 簡(jiǎn)單 | 簡(jiǎn)單場(chǎng)景、兼容性要求高 |
| SSE | 單向 | 是 | 否 | 較簡(jiǎn)單 | 實(shí)時(shí)數(shù)據(jù)展示(如新聞、監(jiān)控) |
| WebSocket | 雙向 | 非常實(shí)時(shí) | 是 | 較復(fù)雜 | 聊天、協(xié)同編輯、游戲等 |
| MQTT | 雙向 | 非常實(shí)時(shí) | 是 | 較復(fù)雜 | 物聯(lián)網(wǎng)、低帶寬環(huán)境 |
| 消息隊(duì)列 + 監(jiān)聽 | 雙向 | 非常實(shí)時(shí) | 是 | 較復(fù)雜 | 分布式系統(tǒng)、微服務(wù) |
八、如何選擇合適的方法?
根據(jù)以下因素進(jìn)行權(quán)衡:
- 是否需要雙向通信?
- 對(duì)實(shí)時(shí)性的要求有多高?
- 客戶端是否支持新技術(shù)(如 WebSocket)?
- 是否有資源限制(如移動(dòng)端、IoT 設(shè)備)?
- 是否已有消息中間件?
九、結(jié)語
隨著 Web 技術(shù)的發(fā)展,服務(wù)端主動(dòng)推送數(shù)據(jù)已經(jīng)不再是難題。Spring Boot 提供了豐富的組件來支持各種推送方式。開發(fā)者應(yīng)根據(jù)業(yè)務(wù)需求、性能考量以及技術(shù)棧特點(diǎn),選擇最適合的推送方案。
如果你正在開發(fā)一個(gè)實(shí)時(shí)性要求高的應(yīng)用,推薦優(yōu)先考慮 WebSocket;如果是簡(jiǎn)單的數(shù)據(jù)流推送,可以嘗試 SSE;而如果必須支持老舊瀏覽器或低頻更新,長(zhǎng)輪詢仍然是一個(gè)可行的選擇。
以上就是SpringBoot環(huán)境下服務(wù)端向客戶端主動(dòng)推送數(shù)據(jù)的幾種常見方式的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot服務(wù)端向客戶端推送數(shù)據(jù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java 無符號(hào)右移與右移運(yùn)算符的使用介紹
這篇文章主要介紹了Java 無符號(hào)右移與右移運(yùn)算符的使用介紹,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
JavaWeb實(shí)現(xiàn)用戶登錄與注冊(cè)功能(服務(wù)器)
這篇文章主要介紹了JavaWeb實(shí)現(xiàn)用戶登錄與注冊(cè)功能,服務(wù)器部分的關(guān)鍵代碼實(shí)現(xiàn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08
Java?Selenium實(shí)現(xiàn)修改打開頁面窗口大小
Selenium是一個(gè)強(qiáng)大的自動(dòng)化測(cè)試工具,支持多種編程語言和瀏覽器,本文將詳細(xì)介紹如何使用Java?Selenium來修改打開頁面窗口的大小,需要的可以參考下2025-01-01
Spring學(xué)習(xí)筆記之RestTemplate使用小結(jié)
這篇文章主要給大家介紹了關(guān)于Spring學(xué)習(xí)筆記之RestTemplate使用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-08-08
Java實(shí)現(xiàn)銀行ATM系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)銀行ATM系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-04-04
使用FeignClient設(shè)置動(dòng)態(tài)Url
這篇文章主要介紹了使用FeignClient設(shè)置動(dòng)態(tài)Url方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06
Java遍歷Properties所有元素的方法實(shí)例
這篇文章主要介紹了Java如何遍歷Properties所有元素的方法,大家可以參考使用2013-11-11
JavaMail實(shí)現(xiàn)郵件發(fā)送機(jī)制
這篇文章主要為大家詳細(xì)介紹了JavaMail實(shí)現(xiàn)郵件發(fā)送機(jī)制,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08

