SSE Performance — Complete Guide to Optimization
In this tutorial, you will learn about SSE Performance. We cover key concepts, practical examples, and best practices to help you master this topic.
SSE performance optimization covers connection pooling, message batching, compression, efficient event serialization, and server tuning to handle thousands of concurrent SSE connections with minimal latency.
What You'll Learn
- Server configuration for high-concurrency SSE
- Message batching and compression strategies
- Monitoring and tuning SSE performance
Why It Matters
Each SSE connection holds a long-lived HTTP connection. Without optimization, 10,000 concurrent SSE connections can exhaust server memory, file descriptors, and CPU.
Real-World Use
Durga Antivirus Pro SSE infrastructure handles 15,000 concurrent connections across 3 servers. Each server runs Node.js with cluster mode, HTTP/2, and gzip compression, serving events with p99 latency under 50ms.
flowchart LR
A["Clients"] --> N["nginx (HTTP/2)"]
N --> W1["Worker 1"]
N --> W2["Worker 2"]
N --> W3["Worker 3"]
W1 --> R["Redis Pub/Sub"]
W2 --> R
W3 --> R
style N fill:#dbeafe,stroke:#2563eb
Code Examples
// Node.js clustered SSE server
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
} else {
const server = http.createServer((req, res) => {
if (req.url === '/events') {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
});
const interval = setInterval(() => {
res.write(`data: ${JSON.stringify({ time: Date.now() })}\n\n`);
}, 1000);
req.on('close', () => clearInterval(interval));
}
});
server.listen(3000);
}
Expected output: Server uses all CPU cores; each worker handles SSE connections independently.
// Message batching for high-throughput SSE
function batchSend(res, events) {
let batch = '';
events.forEach(event => {
batch += `event: ${event.type}\n`;
batch += `id: ${event.id}\n`;
batch += `data: ${JSON.stringify(event.data)}\n\n`;
});
res.write(batch);
}
// Collect events and flush every 100ms or 50 events
let pendingEvents = [];
let flushTimer = setInterval(() => {
if (pendingEvents.length > 0) {
batchSend(res, pendingEvents);
pendingEvents = [];
}
}, 100);
function addEvent(event) {
pendingEvents.push(event);
if (pendingEvents.length >= 50) {
batchSend(res, pendingEvents);
pendingEvents = [];
}
}
Expected output: Events are batched and flushed every 100ms or 50 events, reducing write syscalls.
# nginx SSE performance tuning
server {
listen 443 ssl http2;
serverName sse.example.com;
location /events {
proxy_pass http://sse-backend:3000;
proxy_http_version 1.1;
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 24h;
proxy_send_timeout 24h;
# Performance tuning
proxy_max_temp_file_size 0;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
keepalive_connections 100;
# Connection pooling
upstream sse_backend {
server backend1:3000;
server backend2:3000;
keepalive 256;
}
}
}
Expected output: nginx configured for high-performance SSE with HTTP/2, keepalive, and disabled buffering.
Common Mistakes
1. Single-Threaded Server
Node.js single-threaded servers handle ~10K concurrent connections but CPU-intensive operations block all connections.
2. No Message Batching
Writing each event individually causes excessive syscalls. Batch events every 50-100ms.
3. Memory Leaks from Unclean Connections
Each SSE connection consumes memory. Without proper cleanup on disconnect, memory grows unbounded.
4. Overusing JSON.stringify
Serializing large objects on every event adds CPU overhead. Pre-serialize or use lighter formats.
5. No Connection Pool to Backend
Opening a new backend connection per SSE client exhausts database/Redis connections. Use connection pooling.
Practice Questions
- How does clustering improve SSE server performance?
- Why is message batching important for SSE performance?
- What is the impact of HTTP/2 on SSE performance?
- How do you detect and clean up disconnected SSE clients?
- Why should you pool backend connections for SSE?
Answers:
- Clustering uses all CPU cores, allowing the server to handle more concurrent connections.
- Batching reduces write syscalls and TCP overhead, improving throughput for high-frequency events.
- HTTP/2 multiplexing allows multiple SSE connections over one TCP connection, reducing connection overhead.
- Listen for req.on('close') events and clean up associated timers, intervals, and connection references.
- Each SSE client may subscribe to backend data sources; pooling prevents connection exhaustion.
Challenge: Build a performant SSE server that handles 10,000 concurrent connections. Use clustering, message batching (50ms window), HTTP/2, connection pool to Redis, and proper disconnect cleanup. Load test with 10K concurrent clients.
FAQ
Mini Project
Build a high-performance SSE server: Node.js with clustering, message batching (100ms window), HTTP/2 support, Redis pub/sub backend for cross-worker broadcasting, connection cleanup on close, and metrics endpoint showing active connections and messages/sec.
What's Next
Explore SSE production deployment for running SSE at scale, or learn about SSE nginx configuration for production reverse proxy setup.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro