Lập trình Go 22/09/2026 9 phút

WebSocket server thật trong Go: nâng cấp từ HTTP, giao tiếp hai chiều bền vững

WebSocket nâng cấp một kết nối HTTP thành kênh hai chiều mở liên tục cho chat, thông báo đẩy, dữ liệu real-time. Đo thật trong Go 1.23: bắt tay 101 Switching Protocols, gửi/nhận trên một kết nối bền, và một message qua WebSocket nhanh hơn HTTP polling khoảng 23 lần.

Web 22/09/2026 6 phút

WebSocket: HTTP nâng cấp thành kênh hai chiều — mổ xẻ cái bắt tay 101

WebSocket không phải một giao thức tách rời HTTP mà mọc lên TỪ HTTP: một request GET với header Upgrade, đáp lại là 101 Switching Protocols chứ không phải 200. Bài này tự dựng cả server lẫn client WebSocket bằng socket thuần, đo output thật: cách tính Sec-WebSocket-Accept từ key + GUID, và frame hai chiều mà frame từ client bắt buộc mask.

Lập trình 22/09/2026 8 phút

Polling, long polling, SSE và WebSocket đo thật trên cùng một luồng sự kiện: polling mỗi giây trễ 486 ms và tốn 8,4 KB mỗi phút cho 23 sự kiện, WebSocket và SSE trễ 0,3 ms với 1,3 KB, với 200 client polling tốn CPU gấp ba mà trễ gấp 300 lần

Một server Go phát sự kiện ngẫu nhiên trung bình mỗi 2 giây, một client đo độ trễ từ lúc sự kiện sinh ra tới lúc nhận và đếm byte TCP thật. Polling 1 giây: p50 486 ms, p99 900 ms, 60 request và 8.373 byte mỗi phút. Polling 5 giây: p50 2,3 giây. Long polling: 0,5 ms nhưng 39 request mỗi phút. SSE và WebSocket: 0,3 ms, 1 kết nối, 1.221 và 1.276 byte mỗi phút. Sau netem 50 ms, mọi cách đẩy cộng đúng 50 ms. Với 200 client, polling tốn 0,33 giây CPU server mỗi 30 giây và 12.000 request mỗi phút trong khi SSE tốn 0,11 giây với 200 kết nối mở và 5,4 MB heap.

Lập trình 22/09/2026 10 phút

Khung WebSocket đọc từng byte: header 2 đến 14 byte so với 260 byte của HTTP, mask tốn 0,29 ms mỗi MB nhưng phân mảnh 4 KB mặc định của gorilla làm chậm gấp đôi, và một thông điệp 1 MB chặn thông điệp nhỏ phía sau 436 ms trên cùng kết nối

Client Go đếm byte trên net.Conn và đọc thô từng khung: payload 10 byte tốn 16 byte đi, 12 byte về, so với 151 và 126 byte của HTTP; 1 MB tốn thêm 14 byte, nhưng WriteBufferSize mặc định 4 KB của gorilla cắt thành 256 khung và trả thêm 2.048 byte. Mask không phải nút cổ chai: XOR 1 MB mất 0,29 ms, chiều có mask với buffer 2 MB đạt 2.213 MB/s; phân mảnh 4 KB kéo xuống 1.151 MB/s. Dưới 20 Mbit/s, một thông điệp 1 MB chặn tick phía sau 436 ms trên cùng kết nối; chia 256 thông điệp 4 KB thì tick đến sau 0,5 ms; kết nối riêng vẫn chờ 24 ms. Text và binary nhanh như nhau: gorilla không kiểm UTF-8.

Lập trình 22/09/2026 9 phút

Heartbeat và kết nối chết đo thật: rút cáp bằng iptables, server Go nhận ra sau bao lâu, ping/pong 4,9 giây, TCP keepalive chỉnh tay 8,4 giây, keepalive mặc định của Go 150 giây, tắt keepalive thì không bao giờ, ReadDeadline không ping giết nhầm

Server Go trong dist-lab mở sáu cấu hình WebSocket, client trong go-lab im lặng, rồi rút cáp bằng iptables DROP hai chiều để không có FIN hay RST. Ping mỗi 5 giây với pongWait 8 giây nhận ra sau 4,9 giây; TCP keepalive idle 5 giây, 3 probe cách 2 giây nhận ra sau 8,4 giây; keepalive mặc định của net/http Go sau 150,2 giây, đúng 15 cộng 9 nhân 15; tắt keepalive thì socket còn ESTABLISHED hết 300 giây; server đẩy 20 byte mỗi giây vẫn ghi thành công 300 lần khi ss báo backoff 10. ReadDeadline không ping giết client khoẻ sau đúng 10 giây. Tiến trình client chết thì mọi cấu hình thấy trong 3 ms.

Lập trình 22/09/2026 9 phút

Backpressure WebSocket đo thật: một client không đọc làm hub ghi thẳng đứng hẳn ở giây thứ 3 và bỏ đói 20 client khoẻ, hàng đợi không giới hạn ăn thêm 1 MB RAM mỗi giây, còn hàng đợi 256 với drop hoặc ngắt giữ RAM phẳng ở 13 MB

Server Go fan-out 1 KB, khoảng 900 thông điệp mỗi giây, tới 20 client đọc bình thường và 1 client không bao giờ đọc. Ghi thẳng không deadline: kernel nuốt 2,5 MB vào send buffer rồi Write chặn, vòng hub đứng ở thông điệp 3.133 và cả 20 client khoẻ nhận 0 thông điệp mỗi giây từ giây 4 tới hết. Ghi thẳng với deadline 1 giây: hub kẹt đúng 1.002 ms rồi đóng client chậm ở giây 4,25. Hàng đợi không giới hạn: heap 7,5 lên 26,9 MB, RSS 19 lên 41 MB sau 30 giây, 24.091 thông điệp chờ. Hàng đợi 256 với drop: RSS 13 MB, bỏ 25.076 trong 28.503. Hàng đợi 256 với ngắt: đóng client chậm ở giây 3,82.