How do apps send real-time notifications? | WebSockets vs. Server-Sent Events (SSE)
The Real-Time Problem: Bypassing HTTP Polling
Real-time application updates can be delivered via WebSockets or Server-Sent Events (SSE).
🎯 System Scenario & Requirements
System Goal
Real-time application updates can be delivered via WebSockets or Server-Sent Events (SSE).
Actors & Components
- Application or API tier
- Core service under design
- Operator or SRE observer
Functional Requirements
- TLDR: Real-time application updates can be delivered via WebSockets or Server-Sent Events (SSE).
- WebSockets provide a low-latency, full-duplex TCP connection suitable for bi-directional communication, while SSE utilizes standard HTTP to stream mono-directional server-to-client updates efficiently.
- Standard HTTP is built on a strict Request-Response lifecycle.
Constraints & Assumptions
- Stateful Load Balancing: WebSocket servers are stateful.
- Once a connection is established, the client remains pinned to that specific server instance.
⚡ Execution Stages & Decision Trace
Stage 1 — WebSockets: Full-Duplex Bi-Directional Messaging
WebSockets is a distinct protocol (running over TCP) designed for low-latency, bi-directional, full-duplex communication.
How it works: The Upgrade Handshake A WebSocket connection begins as a standard HTTP request.
Deep Dive
Scenario context
Real-time application updates can be delivered via WebSockets or Server-Sent Events (SSE). WebSockets provide a low-latency, full-duplex TCP connection suitable for bi-directional communication, while SSE utilizes standard HTTP to stream mono-directional server-to-client updates efficiently.
Standard HTTP is built on a strict Request-Response lifecycle. The client initiates a TCP connection, sends an HTTP reque…
This walkthrough focuses on The Real-Time Problem: Bypassing HTTP Polling.
Before making a design choice, clarify:
- Standard HTTP is built on a strict Request-Response lifecycle.
- The client initiates a TCP connection, sends an HTTP request, the server processes it, returns a response, and the connection is closed (or kept alive for subsequent requests).
- The server cannot initiate communication or push data to the client without a prior request.
Concepts in play: system-design-hld-architectures, api-gateway, caching, api-security-oauth2
Stage 1 — WebSockets: Full-Duplex Bi-Directional Messaging
- WebSockets is a distinct protocol (running over TCP) designed for low-latency, bi-directional, full-duplex communication.
- How it works: The Upgrade Handshake A WebSocket connection begins as a standard HTTP request.
- The client sends a handshake request containing specific upgrade headers: If the server supports WebSockets, it responds with an HTTP 101 Switching Protocols status: Once the handshake is complete, the HTTP protocol is…
- The underlying TCP socket remains open, and both the client and server can transmit data frames concurrently.
GET /chat HTTP/1.1
Host: api.abstractalgorithms.dev
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Observable outcome: The underlying TCP socket remains open, and both the client and server can transmit data frames concurrently.
Stage 2 — Server-Sent Events (SSE): Mono-Directional Streaming
- Server-Sent Events (SSE) is a standard web technology that allows a server to stream text-based data to clients over a persistent HTTP connection.
- How it works: The Event Stream Unlike WebSockets, SSE does not upgrade the connection to a new protocol.
- The client opens a request, and the server responds with a specific MIME type: text/event-stream .
- The server keeps this connection open indefinitely.
GET /stream HTTP/1.1
Host: api.abstractalgorithms.dev
Accept: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
event: notification
data: {"message": "You have a new follower!"}
event: ticker
data: {"symbol": "AAPL", "price": 175.50}
Observable outcome: The server keeps this connection open indefinitely.
Stage 3 — Deep Dive: Connection Management and Heartbeats
- In a high-scale real-time system, managing connection state is the primary challenge.
- Because TCP connections can drop silently (e.g., when a mobile device switches towers), both WebSockets and SSE need mechanisms to detect dead connections and free server resources.
- WebSockets Ping/Pong The WebSocket protocol includes built-in control frames for connection checks: - The server periodically sends a Ping frame.
- The client must immediately respond with a Pong frame.
Observable outcome: - The client must immediately respond with a Pong frame.
Stage 4 — Authentication, Resume, and Delivery Semantics
- Authenticate before committing server resources to a long-lived connection.
- Browsers cannot freely attach arbitrary headers to the native WebSocket constructor, so applications commonly use a secure same-site cookie, a short-lived single-use ticket obtained over HTTPS, or a token in the negotia…
- Avoid long-lived bearer tokens in query strings because URLs leak into logs and monitoring systems.
- Token expiry must have an explicit policy: close and reconnect with a refreshed credential, or support a narrowly defined re-authentication message.
Observable outcome: Token expiry must have an explicit policy: close and reconnect with a refreshed credential, or support a narrowly defined re-authentication message.
Stage 5 — Backpressure and Slow Consumers
- A server that produces faster than a client consumes will accumulate buffered messages.
- Set a maximum outbound buffer per connection.
- For replaceable updates such as cursor positions, coalesce to the newest value.
- For durable events, disconnect a slow consumer and let it resume from the event log.
Observable outcome: For durable events, disconnect a slow consumer and let it resume from the event log.
Stage 6 — Scaling Beyond One Server
- Connections are stateful, but event ownership should not live only in process memory.
- Store presence and subscription metadata in a bounded shared system and distribute events through Redis Pub/Sub, Redis Streams, Kafka, NATS, or another broker chosen for the required durability.
- ~~~mermaid flowchart LR C[Clients] -- L[Load balancer] L -- G1[Realtime gateway A] L -- G2[Realtime gateway B] G1 <-- B[Event broker or durable log] G2 <-- B P[Application services] -- B G1 -- S[(Presence and cursor sto…
- Streams or Kafka are better when consumers must resume and replay.
Observable outcome: Streams or Kafka are better when consumers must resume and replay.
End-to-end outcome
After working through the stages, you should be able to explain:
- A persistent connection is not a durable message guarantee.
- SSE simplifies one-way delivery and resume; WebSockets enable richer interaction at higher operational cost.
Alternatives considered
Architectural Comparison
Architectural Comparison
Decision Guide: Selecting the Real-Time Protocol
Use this guide to choose the appropriate protocol for your real-time updates: - Choose WebSockets if: - You are building a bi-directional, high-frequency interactive application (e.g., chat apps, multiplayer games, collaborative editors).
Failure and degraded modes
WebSockets Trade-offs
- Detection: Stateful Load Balancing: WebSocket servers are stateful.
- User or operator impact: Stateful Load Balancing: WebSocket servers are stateful.
SSE Connection Limits
- Detection: A major limitation of SSE is the browser connection limit.
- User or operator impact: A major limitation of SSE is the browser connection limit.
