Backend Development 12 min read

Understanding Server‑Sent Events (SSE) vs WebSocket vs Polling: Choosing the Right Real‑Time Communication Technique

This article compares Server‑Sent Events, WebSocket, and traditional polling, explains why SSE can replace heavyweight WebSocket in one‑way push scenarios, provides configuration and code examples for Spring Boot, and shares practical lessons from real project experiences.

Top Architect
Top Architect
Top Architect
Understanding Server‑Sent Events (SSE) vs WebSocket vs Polling: Choosing the Right Real‑Time Communication Technique

In many projects real‑time communication is required, but developers often default to WebSocket even when only server‑to‑client push is needed. The article introduces Server‑Sent Events (SSE) as a lightweight alternative and compares it with WebSocket and polling.

SSE, Polling, WebSocket detailed comparison:

Feature

SSE (Server‑Sent Events)

Polling

WebSocket

Communication direction

One‑way: server pushes data

One‑way: client periodically requests data

Two‑way: client and server communicate bidirectionally

Connection type

Long‑living HTTP/1.1 or HTTP/2 connection

Short‑lived independent HTTP requests

Long‑living TCP connection (WebSocket handshake)

Transport protocol

HTTP/1.1 or HTTP/2

HTTP

TCP (via WebSocket protocol upgrade)

Browser support

Widely supported (HTML5 native)

All browsers

Widely supported via WebSocket API

Message frequency

Server can push anytime

Client decides frequency

Real‑time bidirectional

Server overhead

Low (single long connection)

High (new connection each poll)

Higher (maintain TCP + heartbeat)

Client overhead

Low (just handle pushes)

High (resource consumption each poll)

Medium (maintain connection)

Data format

Text, JSON, etc.

Text, JSON, etc.

Any format (binary, text)

Reconnection

Automatic on interruption

Manual request needed

Developer must implement

Typical scenarios

Live data push, notifications, monitoring

Low‑frequency, non‑critical updates

Instant messaging, collaborative apps

Complexity

Simple, minimal code

Simple but less performant

Complex (handshake, heartbeat, etc.)

Real‑time

High (immediate push)

Low (depends on poll interval)

High (bidirectional)

Firewall/Proxy compatibility

High (standard HTTP)

High (standard HTTP)

May require special firewall rules

Bandwidth consumption

Low (only data when needed)

High (each poll consumes bandwidth)

Low (data on demand over long connection)

The author then explains why WebSocket was abandoned for a specific ID‑card recognition scenario: the need was only one‑way push, and WebSocket introduced unnecessary complexity such as token handling, reconnection logic, and periodic message sending.

Key reasons to prefer SSE over WebSocket in such cases:

Simpler permission handling – no custom handshake needed.

Less complex logging and connection management.

Minimal code footprint; no extra dependencies.

Automatic reconnection provided by the browser.

No need to implement periodic server‑side push logic.

To demonstrate SSE in a Spring Boot project, the article provides two code examples.

1. Configuring a custom AsyncTaskExecutor (required for production‑grade async support):

@Configuration
@Slf4j
public class WebMvcConfig implements WebMvcConfigurer {
    // Define custom AsyncTaskExecutor Bean
    @Bean(name = "customAsyncTaskExecutor")
    public AsyncTaskExecutor customAsyncTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        int corePoolSize = Runtime.getRuntime().availableProcessors(); // core threads
        int maxPoolSize = corePoolSize * 2; // max threads
        int queueCapacity = 500; // queue size
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setThreadNamePrefix("AsyncExecutor-");
        executor.setWaitForTasksCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        executor.initialize();
        return executor;
    }

    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
        configurer.setTaskExecutor(customAsyncTaskExecutor());
        configurer.setDefaultTimeout(5000);
    }
}

2. Implementing an SSE endpoint that streams online user activity every 5 seconds:

@PreventDuplicateSubmit
@PreAuthorize("@permission.checker('monitor:online-user:list')")
@GetMapping(value = "/user-activity-sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux
streamUserActivitySSE() {
    // Emit a value every 5 seconds
    return Flux.interval(Duration.ofSeconds(5))
        .flatMap(sequence -> Mono.fromCallable(onlineUserService::getUserActivityNum)
            .onErrorReturn(0));
}

The front‑end effect is shown as a GIF where the online user count updates in real time without page reloads.

Later, the author reflects on a past mistake of using polling instead of SSE for the same ID‑card device, noting that a quick SSE implementation would have saved time and avoided a rushed solution.

In conclusion, WebSocket, SSE, and polling each have their own strengths; developers should choose the technique that matches the communication pattern, and many scenarios—especially one‑way notifications—can be efficiently handled with SSE.

backend developmentSpring BootWebSocketReal-time CommunicationServer-Sent EventspollingSSE
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

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.