SpringBoot環(huán)境下服務端向客戶端主動推送數(shù)據(jù)的幾種常見方式
前言
在傳統(tǒng)的 HTTP 請求-響應模型中,客戶端需要主動發(fā)起請求,服務端才能返回數(shù)據(jù)。然而,在某些場景下(如實時聊天、股票行情更新、通知系統(tǒng)等),我們希望服務端能夠主動向客戶端推送數(shù)據(jù)。本文將詳細介紹在 Spring Boot 環(huán)境下實現(xiàn)服務端向客戶端主動推送數(shù)據(jù)的幾種常見方式,并比較它們的優(yōu)缺點和適用場景。
一、什么是“服務端推送”?
“服務端推送”是指服務端在沒有收到客戶端請求的情況下,主動將數(shù)據(jù)發(fā)送給客戶端。這種機制打破了傳統(tǒng) HTTP 的單向通信限制,適用于需要實時交互的場景。
二、常用推送技術(shù)概述
以下是目前主流的幾種服務端向客戶端推送數(shù)據(jù)的方式:
- 長輪詢(Long Polling)
- Server-Sent Events(SSE)
- WebSocket
- MQTT(物聯(lián)網(wǎng)常用)
- 基于消息中間件 + 客戶端監(jiān)聽(如 RabbitMQ + STOMP)
本文主要聚焦于前三種與 Spring Boot 集成較為方便的技術(shù)。
三、方法一:長輪詢(Long Polling)
1. 原理簡介
客戶端定時或持續(xù)發(fā)起請求到服務端,服務端如果無新數(shù)據(jù)則保持連接不返回,直到有數(shù)據(jù)或超時后才響應。客戶端收到響應后立即發(fā)起下一次請求。
2. 優(yōu)點
- 兼容性好,支持所有瀏覽器
- 實現(xiàn)簡單,適合小型項目或低頻更新
3. 缺點
- 連接頻繁建立銷毀,資源消耗大
- 推送延遲較高
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. 原理簡介
SSE 是 HTML5 提供的一種服務器向客戶端推送事件的標準協(xié)議。它是單向通信,即服務端可以不斷向客戶端發(fā)送數(shù)據(jù),但客戶端不能通過該通道向服務端發(fā)送數(shù)據(jù)。
2. 優(yōu)點
- 實時性強
- 協(xié)議輕量,易于實現(xiàn)
- 支持自動重連
3. 缺點
- 只能服務端推送送,不支持雙向通信
- 不兼容 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); // 超時時間
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. 原理簡介
WebSocket 是一種全雙工通信協(xié)議,允許服務端和客戶端之間建立持久連接并隨時互相發(fā)送數(shù)據(jù)。是目前最強大的實時通信解決方案之一。
2. 優(yōu)點
- 實時性強,延遲低
- 支持雙向通信
- 數(shù)據(jù)傳輸效率高
3. 缺點
- 實現(xiàn)相對復雜
- 需要客戶端和服務端都支持 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>
六、其他方式簡要介紹
1. MQTT(適用于物聯(lián)網(wǎng)場景)
- 輕量級發(fā)布/訂閱協(xié)議
- 適用于設備間通信
- 可以結(jié)合 Spring Boot + EMQX / Mosquitto 使用
2. 消息隊列 + 客戶端監(jiān)聽(如 RabbitMQ)
- 服務端將數(shù)據(jù)發(fā)到 MQ
- 客戶端監(jiān)聽某個隊列或 Exchange
- 適合解耦和分布式系統(tǒng)架構(gòu)
七、對比總結(jié)表
| 方法 | 通信方向 | 是否實時 | 是否雙向 | 易用性 | 適用場景 |
|---|---|---|---|---|---|
| 長輪詢 | 單向 | 否(延遲高) | 否 | 簡單 | 簡單場景、兼容性要求高 |
| SSE | 單向 | 是 | 否 | 較簡單 | 實時數(shù)據(jù)展示(如新聞、監(jiān)控) |
| WebSocket | 雙向 | 非常實時 | 是 | 較復雜 | 聊天、協(xié)同編輯、游戲等 |
| MQTT | 雙向 | 非常實時 | 是 | 較復雜 | 物聯(lián)網(wǎng)、低帶寬環(huán)境 |
| 消息隊列 + 監(jiān)聽 | 雙向 | 非常實時 | 是 | 較復雜 | 分布式系統(tǒng)、微服務 |
八、如何選擇合適的方法?
根據(jù)以下因素進行權(quán)衡:
- 是否需要雙向通信?
- 對實時性的要求有多高?
- 客戶端是否支持新技術(shù)(如 WebSocket)?
- 是否有資源限制(如移動端、IoT 設備)?
- 是否已有消息中間件?
九、結(jié)語
隨著 Web 技術(shù)的發(fā)展,服務端主動推送數(shù)據(jù)已經(jīng)不再是難題。Spring Boot 提供了豐富的組件來支持各種推送方式。開發(fā)者應根據(jù)業(yè)務需求、性能考量以及技術(shù)棧特點,選擇最適合的推送方案。
如果你正在開發(fā)一個實時性要求高的應用,推薦優(yōu)先考慮 WebSocket;如果是簡單的數(shù)據(jù)流推送,可以嘗試 SSE;而如果必須支持老舊瀏覽器或低頻更新,長輪詢仍然是一個可行的選擇。
以上就是SpringBoot環(huán)境下服務端向客戶端主動推送數(shù)據(jù)的幾種常見方式的詳細內(nèi)容,更多關(guān)于SpringBoot服務端向客戶端推送數(shù)據(jù)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
一文詳解各種ElasticSearch查詢在Java中的實現(xiàn)
Elasticsearch是用Java開發(fā)的,并作為Apache許可條款下的開放源碼發(fā)布,是當前流行的企業(yè)級搜索引擎,下面這篇文章主要給大家介紹了關(guān)于各種ElasticSearch查詢在Java中實現(xiàn)的相關(guān)資料,需要的朋友可以參考下2023-11-11
java WSDL接口webService實現(xiàn)方式
這篇文章主要為大家詳細介紹了java WSDL接口webService實現(xiàn)方式的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-04-04
SpringBoot整合Redis實現(xiàn)消息發(fā)布與訂閱的示例代碼
能實現(xiàn)發(fā)送與接收信息的中間介有很多,比如:RocketMQ、RabbitMQ、ActiveMQ、Kafka等,本文主要介紹了Redis的推送與訂閱功能并集成Spring Boot的實現(xiàn),感興趣的可以了解一下2022-08-08
Java微信公眾平臺開發(fā)(12) 微信用戶信息的獲取
這篇文章主要為大家詳細介紹了Java微信公眾平臺開發(fā)第十二步,微信用戶信息的獲取,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-04-04

