SpringBoot實(shí)現(xiàn)網(wǎng)頁(yè)消息推送的5種方法對(duì)比
網(wǎng)頁(yè)消息推送(實(shí)時(shí)通知、聊天、狀態(tài)更新、訂單提醒等)是現(xiàn)代 Web 應(yīng)用的核心功能之一。Spring Boot 提供了非常成熟的支持,下面整理出目前最常用、最實(shí)用的 5 種實(shí)現(xiàn)方式,從簡(jiǎn)單到高級(jí)排序,并標(biāo)注適用場(chǎng)景、優(yōu)缺點(diǎn)和核心代碼要點(diǎn)。
1. 短輪詢(Short Polling) + Ajax / Fetch
原理:前端定時(shí)(每 3–10 秒)通過(guò) Ajax / Fetch 向后端拉取新消息。
適用場(chǎng)景:對(duì)實(shí)時(shí)性要求不高、開(kāi)發(fā)周期短、兼容性極強(qiáng)
優(yōu)點(diǎn):實(shí)現(xiàn)最簡(jiǎn)單,無(wú)需額外依賴,兼容所有瀏覽器
缺點(diǎn):服務(wù)器壓力大(大量無(wú)效請(qǐng)求)、延遲高、不夠“實(shí)時(shí)”
核心代碼(后端):
@RestController
public class NotificationController {
@GetMapping("/notifications")
public ResponseEntity<List<String>> getNotifications() {
// 查詢是否有新消息(數(shù)據(jù)庫(kù) / Redis / 內(nèi)存隊(duì)列)
List<String> newMsgs = notificationService.pollNewMessages();
return ResponseEntity.ok(newMsgs);
}
}
前端(Vue/React 通用):
setInterval(async () => {
const res = await fetch('/notifications');
const data = await res.json();
if (data.length > 0) addNotifications(data);
}, 5000);
2. 長(zhǎng)輪詢(Long Polling)
原理:客戶端發(fā)起請(qǐng)求,后端如果沒(méi)有新消息就掛起請(qǐng)求(不返回),有新消息立即返回;前端收到后立即再發(fā)起下一次請(qǐng)求。
適用場(chǎng)景:實(shí)時(shí)性要求較高,但不想引入 WebSocket/SSE
優(yōu)點(diǎn):比短輪詢節(jié)省資源,實(shí)現(xiàn)相對(duì)簡(jiǎn)單
缺點(diǎn):連接頻繁建立/斷開(kāi),服務(wù)器需維護(hù)大量長(zhǎng)連接
后端實(shí)現(xiàn)(使用 DeferredResult):
@GetMapping("/long-poll")
public DeferredResult<List<String>> longPoll() {
DeferredResult<List<String>> result = new DeferredResult<>(30000L); // 30秒超時(shí)
// 異步監(jiān)聽(tīng)新消息(可使用消息隊(duì)列或 BlockingQueue)
messageQueue.addListener(msg -> {
if (!result.isSetOrExpired()) {
result.setResult(Collections.singletonList(msg));
}
});
result.onTimeout(() -> result.setResult(Collections.emptyList()));
return result;
}
3. Server-Sent Events(SSE)
原理:基于 HTTP 的單向服務(wù)器推送,客戶端使用 EventSource 建立持久連接,服務(wù)端通過(guò) text/event-stream 推送事件。
適用場(chǎng)景:服務(wù)器 → 客戶端單向推送(如通知、進(jìn)度、股票價(jià)格、日志監(jiān)控)
優(yōu)點(diǎn):實(shí)現(xiàn)簡(jiǎn)單、自動(dòng)重連、基于標(biāo)準(zhǔn) HTTP、資源消耗比 WebSocket 低
缺點(diǎn):?jiǎn)蜗颍蛻舳瞬荒苤鲃?dòng)發(fā)消息)、不支持二進(jìn)制
后端核心代碼(Spring Boot 推薦方式):
@GetMapping(value = "/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<String>> sseNotifications() {
return Flux.interval(Duration.ofSeconds(1))
.map(seq -> ServerSentEvent.<String>builder()
.id(String.valueOf(seq))
.event("message")
.data("服務(wù)器推送:" + LocalDateTime.now())
.build());
}
或使用 SseEmitter(更靈活,支持?jǐn)嚅_(kāi)重連):
@GetMapping("/notifications")
public SseEmitter stream() {
SseEmitter emitter = new SseEmitter(Long.MAX_VALUE);
// 在其他地方調(diào)用 emitter.send() 推送
return emitter;
}
前端:
const source = new EventSource('/sse');
source.onmessage = e => console.log('收到:', e.data);
4. WebSocket(原生 / @ServerEndpoint)
原理:全雙工通信通道,瀏覽器和服務(wù)器建立持久 TCP 連接。
適用場(chǎng)景:需要雙向?qū)崟r(shí)通信(聊天、協(xié)作編輯、游戲)
優(yōu)點(diǎn):真正的實(shí)時(shí)雙向、低延遲
缺點(diǎn):實(shí)現(xiàn)稍復(fù)雜、需處理斷線重連、心跳
Spring Boot 簡(jiǎn)單實(shí)現(xiàn):
@ServerEndpoint("/ws/{userId}")
@Component
public class WebSocketEndpoint {
private static final Map<String, Session> sessions = new ConcurrentHashMap<>();
@OnOpen
public void onOpen(Session session, @PathParam("userId") String userId) {
sessions.put(userId, session);
}
@OnMessage
public void onMessage(String message, Session session) {
// 廣播或點(diǎn)對(duì)點(diǎn)
}
public static void sendToUser(String userId, String msg) {
Session session = sessions.get(userId);
if (session != null && session.isOpen()) {
session.getAsyncRemote().sendText(msg);
}
}
}
配置:
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new MyWebSocketHandler(), "/my-ws").setAllowedOrigins("*");
}
}
5. WebSocket + STOMP(最推薦的生產(chǎn)方式)
原理:在 WebSocket 上層使用 STOMP 協(xié)議(類似 HTTP 的消息協(xié)議),支持訂閱主題、點(diǎn)對(duì)點(diǎn)、廣播等。
適用場(chǎng)景:幾乎所有中大型實(shí)時(shí)應(yīng)用(聊天、通知、實(shí)時(shí)儀表盤、訂單狀態(tài))
優(yōu)點(diǎn):功能強(qiáng)大(訂閱/取消訂閱、斷線重連、消息確認(rèn))、易于管理用戶會(huì)話、支持廣播/點(diǎn)對(duì)點(diǎn)
依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
配置:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic", "/queue"); // 訂閱前綴
config.setApplicationDestinationPrefixes("/app"); // 發(fā)送前綴
config.setUserDestinationPrefix("/user"); // 點(diǎn)對(duì)點(diǎn)前綴
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws")
.setAllowedOriginPatterns("*")
.withSockJS(); // 支持降級(jí)
}
}
發(fā)送消息(廣播):
@Autowired
private SimpMessagingTemplate messagingTemplate;
public void sendNotification(String message) {
messagingTemplate.convertAndSend("/topic/notifications", message);
}
點(diǎn)對(duì)點(diǎn)(需結(jié)合 Spring Security 或自定義 Principal):
messagingTemplate.convertAndSendToUser(username, "/queue/notifications", message);
前端(使用 stomp.js 或 @stomp/stompjs):
const client = new Client({
brokerURL: 'ws://localhost:8080/ws',
onConnect: () => {
client.subscribe('/topic/notifications', msg => console.log(msg.body));
client.subscribe('/user/queue/notifications', msg => console.log(msg.body));
}
});
client.activate();
總結(jié)對(duì)比表
| 方式 | 實(shí)時(shí)性 | 方向性 | 復(fù)雜度 | 資源消耗 | 推薦場(chǎng)景 |
|---|---|---|---|---|---|
| 短輪詢 | ★☆☆☆☆ | 雙向 | 最低 | 高 | 原型、兼容性要求極高 |
| 長(zhǎng)輪詢 | ★★☆☆☆ | 雙向 | 低 | 中 | 中等實(shí)時(shí)性 |
| SSE | ★★★★☆ | 服務(wù)器→客戶端 | 低 | 低 | 服務(wù)器單向推送首選 |
| 原生 WebSocket | ★★★★★ | 雙向 | 中 | 中 | 簡(jiǎn)單雙向場(chǎng)景 |
| WebSocket + STOMP | ★★★★★ | 雙向 | 中高 | 中 | 生產(chǎn)級(jí)實(shí)時(shí)應(yīng)用首選 |
2026 年推薦優(yōu)先級(jí):
- 大多數(shù)場(chǎng)景 → WebSocket + STOMP
- 純服務(wù)器推送、資源敏感 → SSE
- 簡(jiǎn)單/原型 → 輪詢
到此這篇關(guān)于SpringBoot實(shí)現(xiàn)網(wǎng)頁(yè)消息推送的5種方法對(duì)比的文章就介紹到這了,更多相關(guān)SpringBoot網(wǎng)頁(yè)消息推送內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java方法重載實(shí)現(xiàn)原理及代碼實(shí)例
這篇文章主要介紹了Java方法重載實(shí)現(xiàn)原理及代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09
SpringBoot使用Prometheus實(shí)現(xiàn)監(jiān)控
在當(dāng)今的軟件開(kāi)發(fā)世界中,監(jiān)控是至關(guān)重要的一部分,本文主要介紹了如何在Spring Boot應(yīng)用程序中使用Prometheus進(jìn)行監(jiān)控,以幫助大家更好地理解和管理您的應(yīng)用程序,有需要的可以參考下2023-10-10
spring整合redis實(shí)現(xiàn)數(shù)據(jù)緩存的實(shí)例代碼
這篇文章主要介紹了spring整合redis實(shí)現(xiàn)數(shù)據(jù)緩存,需要的朋友可以參考下2018-09-09
Spring里的Async注解實(shí)現(xiàn)異步操作的方法步驟
這篇文章主要介紹了Spring里的Async注解實(shí)現(xiàn)異步操作的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
java連接Access數(shù)據(jù)庫(kù)的方法
這篇文章主要為大家詳細(xì)介紹了java連接Access數(shù)據(jù)庫(kù)的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05
如何在mapper文件中使用in("str1","str2")
這篇文章主要介紹了如何在mapper文件中使用in("str1","str2"),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01
JAVA求兩直線交點(diǎn)和三角形內(nèi)外心的方法
本文提供了JAVA求兩直線交點(diǎn)、三角形外心、三角形內(nèi)心的代碼和算法講解,大家可以參考使用2013-11-11
Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(43)
下面小編就為大家?guī)?lái)一篇Java基礎(chǔ)的幾道練習(xí)題(分享)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧,希望可以幫到你2021-07-07

