Preface: When building a real-time message push function, choosing the right solution is crucial to developing efficient real-time applications. The push of messages is nothing more than push and pull data models. This article will introduce four common real-time message push solutions: short polling (pull), long polling (pull),
SSE (Server-Sent Events)
(push) andWebSocket
(Push), and uses Spring Boot as the technical base to show how to implement these functions in Java full-stack development.
Article directory
-
- 1. Short Polling
-
- What is short polling?
- Implementation of short polling
- Characteristics and limitations of short polling
- 2. Long Polling
-
- What is long polling?
- Implementation of long polling
- Characteristics and limitations of long polling
- 3. SSE (Server-Sent Events)
-
- What is SSE?
- Implementation of SSE
- Features and limitations of SSE
- 4. WebSocket
-
- What is WebSocket?
- WebSocket implementation
-
- 1. Create a WebSocket handler:
- 2. Configure the WebSocket endpoint:
- 3. Front-end implementation
- Features and limitations of WebSocket
- Summarize
- Notice
1. Short Polling
What is short polling?
Short polling is a simple real-time message push scheme in which the client obtains the latest messages by periodically sending requests to the server. The server responds immediately after receiving the request, regardless of whether there are new messages. If no new messages are available from the server, the client will send the request again.
Implementation of short polling
In Spring Boot, short polling can be implemented through the HTTP interface and scheduled tasks. Here’s a simple example:
// -- Backend interface @GetMapping("/short") public String getShort() {<!-- --> long l = System.currentTimeMillis(); if((l & amp;1) == 1) {<!-- --> return "ok"; } return "fail"; } // --- Front-end page @org.springframework.stereotype.Controller public class Controller {<!-- --> @GetMapping("/s") public String s() {<!-- --> return "s"; } } // -- s.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Short polling</title> </head> <body> <p>msg=<span id="message"></span></p> <script> function pollMessage() {<!-- --> //Send polling request const xhr = new XMLHttpRequest(); xhr.open("GET", "/short", true); xhr.onreadystatechange = function () {<!-- --> if (xhr.readyState === XMLHttpRequest.DONE) {<!-- --> if (xhr.status === 200) {<!-- --> document.getElementById("message").innerHTML = xhr.responseText; } } }; xhr.send(); } setInterval(()=>{<!-- --> pollMessage() }, 1000) </script> </body> </html>
In the above example, the getShort()
method is used to return the message, while the s
method is used to render s.html
. The client can periodically call the getShort()
interface to obtain the latest news.
Characteristics and limitations of short polling
The implementation of short polling is simple, but it has some characteristics and limitations:
- High latency: The client needs to send requests periodically, regardless of whether there are new messages. This can cause some latency, especially if messages are updating slowly.
- High network load: Clients need to send requests frequently, even if messages are not updated. This increases the load on the server and network.
- Poor real-time performance: Since you need to wait for the next poll to get new messages, short polling has relatively poor real-time performance.
2. Long Polling
What is long polling?
Long polling is an improved polling method that keeps requests pending when there are no new messages until a new message arrives or a timeout occurs. Compared with short polling, long polling can obtain new messages faster and reduce unnecessary requests.
Implementation of long polling
In Spring Boot, you can use asynchronous requests and scheduled tasks to implement long polling. Here’s a simple example:
// -- Request interface /** * Long polling * @return */ @GetMapping("/long") public DeferredResult<String> getLong() {<!-- --> DeferredResult<String> deferredResult = new DeferredResult<>(); if (latestMessage != null) {<!-- --> deferredResult.setResult(latestMessage); } else {<!-- --> // Set the timeout using a scheduled task TimerTask timeoutTask = new TimerTask() {<!-- --> @Override public void run() {<!-- --> deferredResult.setResult(null); } }; Timer timer = new Timer(); timer.schedule(timeoutTask, 5000); //Set the timeout to 5 seconds //Set the callback function to be triggered when the message arrives deferredResult.onTimeout(() -> {<!-- --> timer.cancel(); deferredResult.setResult(null); }); deferredResult.onCompletion(timer::cancel); } return deferredResult; } /** * Set message * @param message */ @PostMapping("/send-message") public void sendMessage(@RequestBody String message) {<!-- --> latestMessage = message; } // -- Front-end request <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Long polling</title> </head> <body> <p>msg=<span id="message"></span></p> <p>Number of requests: <span id="cnt"></span></p> <script> var cnt = 0 function pollMessage() {<!-- --> //Send polling request const xhr = new XMLHttpRequest(); xhr.open("GET", "/long", true); xhr.onreadystatechange = function () {<!-- --> if (xhr.readyState === XMLHttpRequest.DONE) {<!-- --> if (xhr.status === 200) {<!-- --> document.getElementById("message").innerHTML = xhr.responseText; } } }; xhr.send(); } setInterval(()=>{<!-- --> + + cnt; document.getElementById('cnt').innerHTML = cnt.toString() pollMessage() }, 5000) </script> </body> </html>
In the above example, the getLong()
method returns a DeferredResult
object, which triggers the callback function when a new message arrives. If no new messages arrive within the timeout period, the DeferredResult
object will return null
.
Characteristics and limitations of long polling
Long polling has the following characteristics and limitations:
- Reduce the number of requests: Long polling can obtain new messages faster, and can reduce the number of unnecessary requests compared to short polling.
- Reduce network load: When there are no new messages, long polling keeps requests pending, reducing frequent requests, thereby reducing the load on the server and network.
- Relative real-time improvement: Long polling can obtain new messages faster. Compared with short polling, the real-time performance is improved. However, you still need to wait for the next poll to get new messages.
3. SSE (Server-Sent Events)
What is SSE?
When using Server-Sent Events (SSE)
, a persistent connection is established between the client (usually a browser) and the server, allowing the server to actively send data to the client. This one-way communication mode in which the server actively pushes data enables real-time updated data to be transmitted to the client in real time without the client making a polling request.
SSE works as follows:
- Establishing a connection: The client creates a connection to the server in the browser by using the
EventSource
object. The client sends an HTTP request to the server, and the header of the request containsAccept: text/event-stream
to indicate that the client wants to receive SSE data. The server responds to this request and establishes a persistent HTTP connection. - Keep connected: The server keeps the connection with the client open and keeps sending data. This connection is one-way and only allows the server to send data to the client. The client cannot send data to the server.
- Server-sent events: The server uses the
Content-Type: text/event-stream
header to indicate that the response is an SSE stream. The server encapsulates the data in a specific SSE format. Each event begins withdata:
, followed by the actual data content, and optional other fields, such asevent:
> andid:
. The data sent by the server can be in any text format, usually JSON. - The client receives events: The client listens to events sent by the server through the
EventSource
object. When the server sends an event, theEventSource
object will trigger the corresponding event handler, and developers can obtain the event data in the handler and perform corresponding operations. A common event is themessage
event, which indicates the receipt of a new message. - Disconnect: When the client no longer needs to receive events from the server, it can close the connection. The client can call the
close()
method of theEventSource
object to explicitly close the connection, or the browser will automatically close the connection when the page is unloaded.
Implementation of SSE
In Spring Boot, you can use the SseEmitter
class to implement SSE. Here’s a simple example:
@RestController public class SSEController {<!-- --> private SseEmitter sseEmitter; @GetMapping("/subscribe") public SseEmitter subscribe() {<!-- --> sseEmitter = new SseEmitter(); return sseEmitter; } @PostMapping("/send-message") public void sendMessage(@RequestBody String message) {<!-- --> try {<!-- --> if (sseEmitter != null) {<!-- --> sseEmitter.send(SseEmitter.event().data(message)); } } catch (IOException e) {<!-- --> e.printStackTrace(); } } } //-s.html <!DOCTYPE html> <html> <head> <title>SSE Demo</title> </head> <body> <h1>SSE Demo</h1> <div id="message-container"></div> <script> //Create an EventSource object and specify the SSE server endpoint var eventSource = new EventSource('/subscribe'); console.log("eventSource=", eventSource) //Listen to the message event and receive messages sent from the server eventSource.addEventListener('message', function(event) {<!-- --> var message = event.data; console.log("message=", message) var messageContainer = document.getElementById('message-container'); messageContainer.innerHTML + = '<p>' + message + '</p>'; }); </script> </body> </html>
In the above example, the client can subscribe to SSE events by accessing the /subscribe
interface, and the server will return a SseEmitter
object. When a new message arrives, call the send()
method of the SseEmitter
object to send the message.
Characteristics and limitations of SSE
SSE has the following features and limitations:
- Better real-time performance: SSE uses persistent connections, which can achieve better real-time performance than short polling and long polling.
- One-way communication: SSE is one-way and only allows the server to push messages to the client. The client cannot send messages to the server.
- Not applicable to lower version browsers: SSE is part of HTML5 and does not support lower version browsers. When using SSE, you need to ensure client browser compatibility.
4. WebSocket
What is WebSocket?
WebSocket is a two-way communication protocol that allows full-duplex communication over a single persistent connection. Different from the previously introduced solutions, WebSocket provides two-way communication capabilities and can achieve real-time two-way data transmission.
WebSocket implementation
In Spring Boot, you can use the Spring WebSocket module to implement WebSocket functionality. Here’s a simple example:
1. Create a WebSocket handler:
@Component public class WebSocketHandler extends TextWebSocketHandler {<!-- --> private List<WebSocketSession> sessions = new CopyOnWriteArrayList<>(); @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception {<!-- --> sessions.add(session); } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {<!-- --> for (WebSocketSession webSocketSession : sessions) {<!-- --> webSocketSession.sendMessage(message); } } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {<!-- --> sessions.remove(session); } }
2. Configure WebSocket endpoint:
@Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer {<!-- --> @Autowired private WebSocketHandler webSocketHandler; @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {<!-- --> registry.addHandler(webSocketHandler, "/websocket").setAllowedOrigins("*"); } }
In the above example, the WebSocketHandler
handler is responsible for handling WebSocket connection, message delivery, and connection closing events. The WebSocketConfig
class is used to configure WebSocket endpoints.
3. Front-end implementation
<!DOCTYPE html> <html> <head> <title>WebSocket Demo</title> </head> <body> <h1>WebSocket Demo</h1> <div id="message-container"></div> <script> //Create a WebSocket object and specify the server URL var socket = new WebSocket('ws://localhost:8080/websocket'); //Listen to WebSocket connection events socket.onopen = function(event) {<!-- --> console.log('WebSocket connected'); }; //Listen to WebSocket message events socket.onmessage = function(event) {<!-- --> var message = event.data; var messageContainer = document.getElementById('message-container'); messageContainer.innerHTML + = '<p>' + message + '</p>'; }; //Listen to the WebSocket closing event socket.onclose = function(event) {<!-- --> console.log('WebSocket closed'); }; //Send message to server function sendMessage() {<!-- --> var messageInput = document.getElementById('message-input'); var message = messageInput.value; socket.send(message); messageInput.value = ''; } </script> <input type="text" id="message-input" placeholder="Enter message"> <button onclick="sendMessage()">Send</button> </body> </html>
Features and limitations of WebSocket
WebSocket has the following characteristics and limitations:
- Best real-time performance: WebSocket provides true two-way communication, which can realize real-time two-way data transmission and has the best real-time performance.
- Low latency: Compared to polling and long polling, WebSocket uses a single persistent connection, reducing the overhead of connection establishment and disconnection, thereby reducing latency.
- Two-way communication: WebSocket allows two-way communication between the server and the client. The server can actively send messages to the client, and the client can also send messages to the server.
- Higher network load: WebSocket uses long connections, which will occupy certain network resources. In large-scale concurrency scenarios, you need to pay attention to the load of the server.
- Browser support: Most modern browsers support WebSocket, but care needs to be taken to consider compatibility across different browsers during development.
Summary
This article introduces four common real-time message push solutions: short polling, long polling, SSE
and WebSocket
, and uses Spring Boot as the technical base to show how to Implement these functions in Java full-stack development.
- Short polling is a simple real-time message push solution, but it has the limitations of high latency, high network load and poor real-time performance.
- Long polling improves real-time performance by reducing the number of unnecessary requests by keeping requests pending, but polling is still required to obtain new messages.
- SSE uses persistent connections to implement one-way real-time message push, which has good real-time performance, but only supports one-way communication from the server to the client.
- WebSocket provides true two-way communication with optimal real-time performance and low latency, but requires attention to higher network load and browser compatibility.
Choosing an appropriate real-time message push solution depends on specific needs and scenarios. Based on the application requirements and expected user experience, developers can choose an appropriate solution to implement real-time message push functionality.
Note
The above implementations all belong to the demo
level. For simple demonstration, all service assurance measures have been deleted, so there are shortcomings including but not limited to the following.
for example
sse
- No session management
- Clear text transmission
WebSocket
-
To distinguish client connections, the current implementation belongs to message broadcasting.
-
Connection reliability guarantee: heartbeat detection and automatic reconnection, etc.
-
Clear text transmission of messages