高效推送!Spring Boot 3.4 实现网页消息通知的 5 种最佳方案
在现代应用开发中,实时消息推送已成为提升用户体验的重要手段。无论是在线聊天、系统通知、金融数据更新,还是团队协作,服务器主动向浏览器推送信息的能力至关重要。本文将详细介绍 Spring Boot 3.4 中实现网页消息推送的五种主流方案,帮助开发者选择最适合的技术方案。
为什么需要消息推送?
传统的 HTTP 请求是典型的客户端-服务器交互模式,即客户端发起请求,服务器返回响应。然而,在许多业务场景下,我们希望服务器能够主动向客户端推送消息,例如:
-
在线聊天系统
-
股票、基金等金融数据的实时更新
-
业务系统的通知提醒
-
在线文档的协同编辑
-
......
消息推送的五种方案
1. 短轮询(Short Polling)
工作原理
客户端定期向服务器发送请求,检查是否有新消息。
Spring Boot 3.4 实现
package com.icoderoad.controller;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@RestController
@RequestMapping("/api/messages")
public class MessageController {
private final Map<String, List<String>> userMessages = new ConcurrentHashMap<>();
@GetMapping("/{userId}")
public List<String> getMessages(@PathVariable String userId) {
List<String> messages = userMessages.getOrDefault(userId, new ArrayList<>());
List<String> result = new ArrayList<>(messages);
messages.clear(); // 清空已读消息
return result;
}
@PostMapping("/{userId}")
public void sendMessage(@PathVariable String userId, @RequestBody String message) {
userMessages.computeIfAbsent(userId, k -> new ArrayList<>()).add(message);
}
}
前端代码
function startPolling(){
setInterval(()=>{
fetch('/api/messages/user123')
.then(response=> response.json())
.then(messages=>{
if(messages.length>0){
messages.forEach(msg=>console.log(msg));
}
});
},3000);// 每 3 秒查询一次
}
优缺点
优点:
-
实现简单,适用于大部分浏览器
-
兼容性极好,无需特殊服务器配置
缺点:
-
资源消耗大,存在大量无效请求
-
实时性较差,受轮询间隔影响
-
服务器负载高,在用户量大时不适用
2. 长轮询(Long Polling)
工作原理
客户端发起请求,若服务器无新消息,则保持连接打开,直到有消息或超时。
Spring Boot 3.4 实现
package com.icoderoad.controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.DeferredResult;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@RestController
@RequestMapping("/api/long-polling")
public class LongPollingController {
private final Map<String, DeferredResult<List<String>>> waitingRequests = new ConcurrentHashMap<>();
private final Map<String, List<String>> pendingMessages = new ConcurrentHashMap<>();
@GetMapping("/{userId}")
public DeferredResult<List<String>> waitForMessages(@PathVariable String userId) {
DeferredResult<List<String>> result = new DeferredResult<>(60000L, new ArrayList<>());
List<String> messages = pendingMessages.get(userId);
if (messages != null && !messages.isEmpty()) {
List<String> messagesToSend = new ArrayList<>(messages);
messages.clear();
result.setResult(messagesToSend);
} else {
waitingRequests.put(userId, result);
result.onCompletion(() -> waitingRequests.remove(userId));
result.onTimeout(() -> waitingRequests.remove(userId));
}
return result;
}
}
优缺点
优点:
-
比短轮询更高效,减少无效请求
-
近实时响应
缺点:
-
服务器资源占用较大
-
不适用于大规模并发请求
3. Server-Sent Events(SSE)
工作原理
服务器通过单向流向客户端推送数据。
Spring Boot 3.4 实现
package com.icoderoad.controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
@RestController
@RequestMapping("/api/sse")
public class SSEController {
private final Map<String, SseEmitter> emitters = new ConcurrentHashMap<>();
@GetMapping("/subscribe/{userId}")
public SseEmitter subscribe(@PathVariable String userId) {
SseEmitter emitter = new SseEmitter(Long.MAX_VALUE);
emitters.put(userId, emitter);
return emitter;
}
@PostMapping("/publish/{userId}")
public void publish(@PathVariable String userId, @RequestBody String message) throws IOException {
SseEmitter emitter = emitters.get(userId);
if (emitter != null) {
emitter.send(SseEmitter.event().name("MESSAGE").data(message));
}
}
}
优缺点
优点:
-
服务器主动推送,减少客户端请求
-
自动重连
缺点:
-
仅支持单向通信
-
不支持 IE 浏览器
4. WebSocket
工作原理
WebSocket 允许服务器与客户端建立双向连接。
Spring Boot 3.4 实现
package com.icoderoad.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.*;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new MessageWebSocketHandler(), "/ws/messages").setAllowedOrigins("*");
}
}
优缺点
优点:
-
全双工通信,实时性最强
-
适用于高频交互场景
缺点:
-
需要浏览器和服务器都支持 WebSocket
-
可能需要负载均衡支持
5.基于 MQTT 的消息推送
工作原理
MQTT 是一种轻量级的消息传输协议,基于 发布/订阅 机制,适用于低带宽、高延迟或不稳定的网络环境。服务器(Broker)负责消息的转发,客户端可以订阅特定的主题(Topic),当有新消息发布时,Broker 会自动推送给所有订阅者。
Spring Boot 3.4 + MQTT 实现
1. 引入依赖
在 pom.xml
中添加 Eclipse Paho MQTT 客户端:
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.5</version>
</dependency>
2. 配置 MQTT 连接
在 application.yml
添加 MQTT 服务器的配置信息:
mqtt:
broker: tcp://localhost:1883
clientId: spring-boot-mqtt-client
topic: /notifications
username: admin
password: secret
3. 编写 MQTT 配置类
package com.icoderoad.config;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.stereotype.Component;
@Component
public class MqttConfig {
private MqttClient client;
public MqttConfig() throws MqttException {
String brokerUrl = "tcp://localhost:1883";
String clientId = "spring-boot-mqtt-client";
client = new MqttClient(brokerUrl, clientId, new MemoryPersistence());
MqttConnectOptions options = new MqttConnectOptions();
options.setCleanSession(true);
options.setUserName("admin");
options.setPassword("secret".toCharArray());
client.connect(options);
}
public void publishMessage(String topic, String message) throws MqttException {
client.publish(topic, message.getBytes(), 2, false);
}
}
4. 消息发布接口
package com.icoderoad.controller;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/mqtt")
public class MqttController {
private final MqttConfig mqttConfig;
public MqttController(MqttConfig mqttConfig) {
this.mqttConfig = mqttConfig;
}
@PostMapping("/publish")
public String publishMessage(@RequestParam String topic, @RequestBody String message) {
try {
mqttConfig.publishMessage(topic, message);
return "消息已发送";
} catch (MqttException e) {
return "发送失败:" + e.getMessage();
}
}
}
5. 客户端订阅 MQTT 消息
前端 JavaScript 代码(使用 mqtt.js
):
const mqtt =require('mqtt');
const client = mqtt.connect('ws://localhost:9001');
client.on('connect',()=>{
console.log('已连接到 MQTT Broker');
client.subscribe('/notifications',(err)=>{
if(!err){
console.log('成功订阅 /notifications 主题');
}
});
});
client.on('message',(topic, message)=>{
console.log(`收到消息: ${message.toString()}`);
});
MQTT 方案的优缺点
优点:
- 高效
基于发布/订阅模式,支持大规模并发推送,性能优越。
- 轻量
协议数据包小,适用于 IoT 和移动端推送。
- 稳定性强
即使客户端掉线,MQTT 也支持 QoS 质量保证,可以保证消息传递。
缺点:
- 服务器需要额外部署 MQTT Broker(如 Mosquitto)
- 不适用于短连接场景
(如一次性通知)。
结论
不同方案适用于不同场景,开发者应根据业务需求选择合适的技术方案。在 Spring Boot 3.4 中,WebSocket 是最实时的方案,而 SSE 适用于单向推送,长轮询和短轮询则适用于兼容性要求较高的场景。