Mastering Web Message Push: From Short Polling to SSE and WebSocket
This article explains various web message‑push techniques—including short polling, long polling, iframe streaming, Server‑Sent Events, MQTT, and WebSocket—detailing their principles, advantages, and step‑by‑step Spring Boot and JavaScript implementations with complete code examples.
What Is Message Push (push)
Message push refers to the proactive delivery of notifications from a server to a user's web page or mobile app, such as a red dot indicating unread messages.
Short Polling
Short polling repeatedly sends HTTP requests at fixed intervals to fetch unread message counts. A simple JavaScript timer can request the count every second:
<code>setInterval(() => {
// request unread count
messageCount().then(res => {
if (res.code === 200) {
this.messageCount = res.data;
}
});
}, 1000);
</code>While easy, short polling creates unnecessary load because requests are sent even when no new messages exist.
Long Polling
Long polling keeps the request open until the server has new data, reducing load while maintaining near‑real‑time updates. The implementation uses Spring Boot's
DeferredResultand a Guava
Multimapto manage pending requests:
<code>@Controller
@RequestMapping("/polling")
public class PollingController {
public static Multimap<String, DeferredResult<String>> watchRequests =
Multimaps.synchronizedMultimap(HashMultimap.create());
@GetMapping("watch/{id}")
@ResponseBody
public DeferredResult<String> watch(@PathVariable String id) {
DeferredResult<String> dr = new DeferredResult<>(TIME_OUT);
dr.onCompletion(() -> watchRequests.remove(id, dr));
watchRequests.put(id, dr);
return dr;
}
@GetMapping("publish/{id}")
@ResponseBody
public String publish(@PathVariable String id) {
if (watchRequests.containsKey(id)) {
for (DeferredResult<String> dr : watchRequests.get(id)) {
dr.setResult("Update " + new Date());
}
}
return "success";
}
}
</code>If the request times out, an
AsyncRequestTimeoutExceptionis caught globally with
@ControllerAdviceto return a 304 status, prompting the client to re‑initiate the poll.
Iframe Streaming
Iframe streaming inserts a hidden
<iframe>whose
srcpoints to an endpoint that continuously writes HTML/JavaScript snippets to the response, updating the page in real time. Example client markup:
<code><iframe src="/iframe/message" style="display:none"></iframe>
</code>Server side continuously writes script tags with updated data.
Server‑Sent Events (SSE)
SSE uses a single‑direction HTTP connection that streams
text/event-streamdata to the browser. It requires only standard HTTP support and automatically handles reconnection. Client example:
<code>let source = null;
if (window.EventSource) {
source = new EventSource('http://localhost:7777/sse/sub/' + userId);
source.addEventListener('open', () => console.log('Connection opened'));
source.addEventListener('message', e => setMessageInnerHTML(e.data));
} else {
console.log('Your browser does not support SSE');
}
</code>Server side creates an
SseEmitter, stores it in a map, and sends messages via
emitter.send(message):
<code>private static Map<String, SseEmitter> sseEmitterMap = new ConcurrentHashMap<>();
public static SseEmitter connect(String userId) {
SseEmitter emitter = new SseEmitter(0L);
emitter.onCompletion(() -> sseEmitterMap.remove(userId));
emitter.onError(e -> sseEmitterMap.remove(userId));
sseEmitterMap.put(userId, emitter);
return emitter;
}
public static void sendMessage(String userId, String message) {
if (sseEmitterMap.containsKey(userId)) {
try { sseEmitterMap.get(userId).send(message); }
catch (IOException e) { sseEmitterMap.remove(userId); }
}
}
</code>SSE is not supported in IE but works in modern browsers.
MQTT
MQTT (Message Queue Telemetry Transport) is a lightweight publish/subscribe protocol built on TCP/IP, ideal for IoT scenarios where bandwidth is limited and devices need asynchronous, reliable messaging.
WebSocket
WebSocket establishes a full‑duplex TCP connection, enabling bidirectional communication. In Spring Boot, a WebSocket server is annotated with
@ServerEndpointand manages sessions in a thread‑safe set:
<code>@Component
@ServerEndpoint("/websocket/{userId}")
public class WebSocketServer {
private Session session;
private static final CopyOnWriteArraySet<WebSocketServer> webSockets = new CopyOnWriteArraySet<>();
private static final Map<String, Session> sessionPool = new HashMap<>();
@OnOpen
public void onOpen(Session session, @PathParam("userId") String userId) {
this.session = session;
webSockets.add(this);
sessionPool.put(userId, session);
}
@OnMessage
public void onMessage(String message) {
// handle incoming message
}
public void sendOneMessage(String userId, String message) {
Session s = sessionPool.get(userId);
if (s != null && s.isOpen()) {
s.getAsyncRemote().sendText(message);
}
}
}
</code>Client side uses the standard
WebSocketAPI to open a connection, send messages, and handle events.
Choosing the Right Push Technique
There is no universally best method; selection depends on requirements such as bidirectional communication, browser support, implementation complexity, and scalability.
Overall, the article provides six practical push solutions with complete Spring Boot and JavaScript code, guiding developers to implement real‑time notifications tailored to their specific application scenarios.
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.