目录
6. MQTT(Message Queuing Telemetry Transport)
在 Web 开发中,实时推送消息是一个常见的需求,以下为你介绍 Java 实现 Web 实时推送消息的 7 种方式:
1. 轮询(Polling)
解释:客户端定时向服务器发送请求,检查是否有新消息。这种方式简单但效率低,会产生大量不必要的请求。
代码示例:
- 前端(HTML + JavaScript):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Polling Example</title>
</head>
<body>
<div id="messages"></div>
<script>
function poll() {
fetch('/messages')
.then(response => response.text())
.then(data => {
document.getElementById('messages').innerHTML = data;
setTimeout(poll, 5000); // 每5秒轮询一次
});
}
poll();
</script>
</body>
</html>
- 后端(Spring Boot):
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MessageController {
@GetMapping("/messages")
public String getMessages() {
// 模拟返回消息
return "New message at " + System.currentTimeMillis();
}
}
2. 长轮询(Long Polling)
解释:客户端向服务器发送请求,服务器如果没有新消息则保持连接,直到有新消息或超时,然后返回消息,客户端收到消息后再发起新的请求。
代码示例:
- 前端(HTML + JavaScript):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Long Polling Example</title>
</head>
<body>
<div id="messages"></div>
<script>
function longPoll() {
fetch('/long-poll')
.then(response => response.text())
.then(data => {
document.getElementById('messages').innerHTML = data;
longPoll(); // 收到消息后再次发起请求
});
}
longPoll();
</script>
</body>
</html>
- 后端(Spring Boot):
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
@RestController
public class LongPollingController {
@GetMapping("/long-poll")
public String longPoll() {
try {
// 模拟等待新消息
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "New message after long poll";
}
}
3. Server-Sent Events(SSE)
解释:服务器可以向客户端发送实时更新,客户端使用EventSource
对象监听服务器发送的事件。SSE 是单向通信,只能服务器向客户端发送消息。
代码示例:
- 前端(HTML + JavaScript):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>SSE Example</title>
</head>
<body>
<div id="messages"></div>
<script>
const eventSource = new EventSource('/sse');
eventSource.onmessage = function(event) {
document.getElementById('messages').innerHTML += event.data + '<br>';
};
</script>
</body>
</html>
- 后端(Spring Boot):
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import java.time.Duration;
import java.util.concurrent.atomic.AtomicInteger;
@RestController
public class SSEController {
@GetMapping(path = "/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<Integer>> streamEvents() {
AtomicInteger counter = new AtomicInteger(0);
return Flux.interval(Duration.ofSeconds(1))
.map(seq -> ServerSentEvent.<Integer>builder()
.data(counter.incrementAndGet())
.build());
}
}
4. WebSocket
解释:WebSocket 是一种双向通信协议,允许浏览器和服务器之间进行实时通信。建立连接后,双方可以随时发送消息。
代码示例:
- 前端(HTML + JavaScript):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebSocket Example</title>
</head>
<body>
<div id="messages"></div>
<input type="text" id="messageInput">
<button onclick="sendMessage()">Send</button>
<script>
const socket = new WebSocket('ws://localhost:8080/ws');
socket.onmessage = function(event) {
document.getElementById('messages').innerHTML += event.data + '<br>';
};
function sendMessage() {
const message = document.getElementById('messageInput').value;
socket.send(message);
}
</script>
</body>
</html>
- 后端(Spring Boot):
import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.io.IOException;
@Component
public class WebSocketHandler extends TextWebSocketHandler {
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws IOException {
String payload = message.getPayload();
session.sendMessage(new TextMessage("Server received: " + payload));
}
}
5. STOMP over WebSocket
解释:STOMP(Simple Text Oriented Messaging Protocol)是一个简单的文本消息协议,基于 WebSocket 提供更高级的消息传递功能,如消息队列、主题订阅等。
代码示例:
- 前端(HTML + JavaScript):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>STOMP over WebSocket Example</title>
</head>
<body>
<div id="messages"></div>
<input type="text" id="messageInput">
<button onclick="sendMessage()">Send</button>
<script src="https://cdn.jsdelivr.net/npm/@stomp/stompjs@6.1.2/bundles/stomp.umd.min.js"></script>
<script>
const socket = new WebSocket('ws://localhost:8080/stomp');
const client = Stomp.over(socket);
client.connect({}, function(frame) {
client.subscribe('/topic/messages', function(message) {
document.getElementById('messages').innerHTML += message.body + '<br>';
});
});
function sendMessage() {
const message = document.getElementById('messageInput').value;
client.send('/app/send', {}, message);
}
</script>
</body>
</html>
- 后端(Spring Boot):
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/stomp").withSockJS();
}
}
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
@Controller
public class StompController {
@MessageMapping("/send")
@SendTo("/topic/messages")
public String handleMessage(String message) {
return "Server received: " + message;
}
}
6. MQTT(Message Queuing Telemetry Transport)
解释:MQTT 是一种轻量级的消息传输协议,适用于低带宽、不稳定网络环境。客户端可以订阅主题,服务器向订阅者推送消息。
代码示例:
- 前端(HTML + JavaScript):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>MQTT Example</title>
</head>
<body>
<div id="messages"></div>
<input type="text" id="messageInput">
<button onclick="sendMessage()">Send</button>
<script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
<script>
const client = mqtt.connect('ws://localhost:1884');
client.subscribe('test/topic');
client.on('message', function(topic, message) {
document.getElementById('messages').innerHTML += message.toString() + '<br>';
});
function sendMessage() {
const message = document.getElementById('messageInput').value;
client.publish('test/topic', message);
}
</script>
</body>
</html>
- 后端(Java):
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
public class MQTTServer {
public static void main(String[] args) {
String broker = "tcp://localhost:1883";
String clientId = "JavaServer";
MemoryPersistence persistence = new MemoryPersistence();
try {
MqttClient sampleClient = new MqttClient(broker, clientId, persistence);
MqttConnectOptions connOpts = new MqttConnectOptions();
connOpts.setCleanSession(true);
sampleClient.connect(connOpts);
sampleClient.subscribe("test/topic");
sampleClient.setCallback(new MqttCallback() {
@Override
public void connectionLost(Throwable cause) {
System.out.println("Connection lost: " + cause.getMessage());
}
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
System.out.println("Received message: " + new String(message.getPayload()));
// 可以在这里进行消息处理和推送
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
System.out.println("Delivery complete");
}
});
} catch (MqttException me) {
me.printStackTrace();
}
}
}
7. Redis Pub/Sub
解释:Redis 的发布 / 订阅机制允许客户端订阅频道,发布者向频道发布消息,订阅者会收到消息。
代码示例:
- 后端(Spring Boot):
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.stereotype.Service;
@Service
public class RedisMessagePublisher {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private ChannelTopic topic;
public void publish(String message) {
redisTemplate.convertAndSend(topic.getTopic(), message);
}
}
@Service
public class RedisMessageSubscriber implements MessageListener {
@Override
public void onMessage(Message message, byte[] pattern) {
System.out.println("Received message: " + new String(message.getBody()));
// 可以在这里进行消息处理和推送
}
}
以上就是 Java 实现 Web 实时推送消息的 7 种方式,你可以根据具体的业务需求选择合适的方式。