uWebSockets.js vs ws vs Socket.IO: Benchmark 10.000+ kết nối và RAM thực tế
So sánh uWebSockets.js, ws và Socket.IO bằng benchmark 10.000+ WebSocket connections: phương pháp test, server mẫu, RAM/RSS, throughput, latency, backpressure và cách chọn.

So sánh uWebSockets.js, ws và Socket.IO bằng benchmark 10.000+ WebSocket connections: phương pháp test, server mẫu, RAM/RSS, throughput, latency, backpressure và cách chọn.

Đây là giả thuyết cần xác minh, không phải số liệu universal.
RAM và throughput thay đổi mạnh theo:
Tài liệu Socket.IO công khai benchmark tới 10.000 clients trên Ubuntu 22.04, Node.js 20.3.0 với Socket.IO 4.7.2, uWebSockets.js 20.33.0 và ws 8.11.0; chính tài liệu cũng nhấn mạnh memory tăng gần tuyến tính theo số client. Đây là reference hữu ích nhưng version/hardware phải được ghi lại khi áp dụng.
Không đo một scenario duy nhất. Chạy ít nhất bốn workload:
Sau baseline, chạy riêng:
Không đặt 10.000 clients và server trên cùng một Node.js process. Tốt nhất:
Kiểm tra file descriptor:
Cho môi trường benchmark có kiểm soát:
ip_local_port_range đặc biệt quan trọng trên load generator. Một source IP bị giới hạn bởi ephemeral ports; muốn vượt cao có thể cần nhiều source IP/máy.
Ghi lại:
Không thay sysctl production chỉ để “đẹp benchmark” nếu chưa hiểu ảnh hưởng vận hành.
ws có optional bufferutil để tăng tốc masking/unmasking. Nếu bật, phải bật và ghi rõ thành một candidate riêng; đừng để máy này có addon còn máy khác không.
Pin đúng git tag/commit của uWebSockets.js vì dự án được phân phối khác package npm thông thường. Ghi lại commit trong report.
Client load test phải dùng socket.io-client:
Nếu production cần HTTP long-polling fallback, chạy thêm candidate Socket.IO default transports thay vì đánh đồng với WebSocket-only.
Raw WebSocket gửi 128 byte nhưng Socket.IO gửi object JSON có metadata thì không còn apples-to-apples.
Hai bài test hợp lệ:
Cần publish cả hai; transport benchmark cho biết trần kỹ thuật, application benchmark cho biết capacity thực.
heapUsed không phải tổng RAM. Native addon, Buffer, TLS và kernel socket buffers có thể nằm ngoài V8 heap. Tối thiểu ghi:
Ở OS/cgroup:
Công thức memory tăng thêm trên mỗi connection:
Báo cả median và peak RSS. Đợi connection ổn định, chạy GC tự nhiên và không dùng một snapshot ngay sau ramp-up làm kết luận.
Không điền số từ máy khác. Report của bạn nên có dạng:
Mỗi ô là median của ít nhất 5 run; p99 nên tính từ HDR histogram hoặc histogram có độ phân giải phù hợp, không chỉ từ average.
Đây là kết quả thường được kỳ vọng do core native tối ưu và data path ngắn. Nhưng cần kiểm tra:
Nếu một process ws giữ 10k connection với p99/SLO đạt yêu cầu và CPU còn headroom, chuyển sang uWS chỉ để thắng benchmark có thể không đáng migration risk.
Điều này phản ánh Engine.IO/Socket.IO session, event packet, rooms và feature state. Câu hỏi đúng không phải “tại sao raw WS nhẹ hơn?” mà là:
Chi phí hạ tầng tăng thêm có nhỏ hơn chi phí tự xây reconnect, rooms, acknowledgement và client compatibility không?
Kết quả không đo server. Scale generator ngang, giảm client-side metrics hoặc dùng generator native hiệu quả hơn. Theo dõi CPU hai phía trong mọi run.
ws ghi rõ permessage-deflate tạo overhead đáng kể về performance và memory, và compression concurrency trên Linux có thể dẫn tới memory fragmentation. Vì vậy:
Socket.IO/Engine.IO và uWebSockets.js cũng cần cấu hình compression tương đương; “default khác nhau” không phải benchmark công bằng.
10.000 client nhanh không giống 9.999 client nhanh + 1 client rất chậm.
Mỗi implementation phải có policy:
Một server có throughput rất cao bằng cách queue vô hạn trong RAM không phải server nhanh; đó là memory leak có độ trễ.
Những nguồn này có thể thay đổi version và chart. Luôn ghi ngày truy cập, commit và môi trường trong report.
bytes_per_connection = (steady_RSS_at_10000 - baseline_RSS_at_0) / 10000uWebSockets.js → tối đa hóa transport efficiencyws → raw WebSocket cân bằng và dễ tích hợpSocket.IO → trả overhead để lấy feature và developer velocityconnections: [1000, 5000, 10000, 25000]ramp_up: 300ssteady_state: 600spayloads: [128B, 1KiB, 16KiB]echo_rates: [1, 10] messages/client/secondbroadcast_rates: [1, 10, 100] messages/secondcompression: falsetls: falserepetitions: 5ulimit -nulimit -n 200000sudo sysctl -w net.core.somaxconn=65535sudo sysctl -w net.ipv4.ip_local_port_range='1024 65535'uname -anode --versionlscpufree -hsysctl net.core.somaxconnulimit -nimport { WebSocketServer } from 'ws';const wss = new WebSocketServer({ port: 8080, perMessageDeflate: false, maxPayload: 64 * 1024,});wss.on('connection', socket => { socket.on('error', () => {}); socket.on('message', (data, isBinary) => { if (socket.readyState === socket.OPEN) { socket.send(data, { binary: isBinary }); } });});import uWS from 'uWebSockets.js';uWS.App() .ws('/*', { compression: uWS.DISABLED, maxPayloadLength: 64 * 1024, maxBackpressure: 1024 * 1024, idleTimeout: 60, message: (socket, message, isBinary) => { socket.send(message, isBinary, false); }, drain: socket => { // Theo dõi socket.getBufferedAmount() nếu workload tạo backpressure. }, }) .listen(8080, token => { if (!token) throw new Error('Cannot listen on 8080'); console.log('uWebSockets.js listening on 8080'); });import { Server } from 'socket.io';const io = new Server(8080, { transports: ['websocket'], allowUpgrades: false, perMessageDeflate: false, httpCompression: false, maxHttpBufferSize: 64 * 1024,});io.engine.on('connection', rawSocket => { // Chỉ dùng nếu application không cần request object về sau. rawSocket.request = null;});io.on('connection', socket => { socket.on('echo', payload => { socket.emit('echo', payload); });});const socket = io('http://server:8080', { transports: ['websocket'], upgrade: false, reconnection: false,});setInterval(() => { const m = process.memoryUsage(); console.log(JSON.stringify({ rssMB: m.rss / 1024 / 1024, heapUsedMB: m.heapUsed / 1024 / 1024, externalMB: m.external / 1024 / 1024, arrayBuffersMB: m.arrayBuffers / 1024 / 1024, }));}, 5000).unref();pidstat -r -u -p SERVER_PID 1cat /proc/SERVER_PID/status | grep -E 'VmRSS|VmHWM|Threads'cat /sys/fs/cgroup/memory.currentNhững bài viết có cùng chủ đề hoặc công nghệ bạn có thể muốn đọc tiếp.


