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