Backend Development 18 min read

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.

macrozheng
macrozheng
macrozheng
Mastering Web Message Push: From Short Polling to SSE and WebSocket

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

DeferredResult

and a Guava

Multimap

to manage pending requests:

<code>@Controller
@RequestMapping("/polling")
public class PollingController {
    public static Multimap&lt;String, DeferredResult&lt;String&gt;&gt; watchRequests =
        Multimaps.synchronizedMultimap(HashMultimap.create());

    @GetMapping("watch/{id}")
    @ResponseBody
    public DeferredResult&lt;String&gt; watch(@PathVariable String id) {
        DeferredResult&lt;String&gt; 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&lt;String&gt; dr : watchRequests.get(id)) {
                dr.setResult("Update " + new Date());
            }
        }
        return "success";
    }
}
</code>

If the request times out, an

AsyncRequestTimeoutException

is caught globally with

@ControllerAdvice

to return a 304 status, prompting the client to re‑initiate the poll.

Iframe Streaming

Iframe streaming inserts a hidden

&lt;iframe&gt;

whose

src

points to an endpoint that continuously writes HTML/JavaScript snippets to the response, updating the page in real time. Example client markup:

<code>&lt;iframe src="/iframe/message" style="display:none"&gt;&lt;/iframe&gt;
</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-stream

data 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&lt;String, SseEmitter&gt; 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

@ServerEndpoint

and manages sessions in a thread‑safe set:

<code>@Component
@ServerEndpoint("/websocket/{userId}")
public class WebSocketServer {
    private Session session;
    private static final CopyOnWriteArraySet&lt;WebSocketServer&gt; webSockets = new CopyOnWriteArraySet<>();
    private static final Map&lt;String, Session&gt; 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

WebSocket

API 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.

JavaSpring BootWebSocketweb pushlong pollingSSE
macrozheng
Written by

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.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.