Load Balancing Algorithms: Round Robin, Weighted, Least Connections và IP Hash
Hướng dẫn lựa chọn thuật toán Load Balancing: Round Robin, Weighted Round Robin, Least Connections và IP Hash; kèm TypeScript, NGINX, health checks và HTTP/2 caveats.
ShanverseUpdated 11 min read— viewsLoading views
Load Balancing
Deployment
Load Balancer thật sự cân bằng điều gì?
Load Balancer đứng giữa client và backend pool:
Mermaid
Loading diagram…
Nhưng “chia đều” có thể mang nhiều nghĩa:
Số request bằng nhau.
Số connection đang mở bằng nhau.
Tỷ lệ theo capacity của server.
CPU, queue depth hoặc latency gần nhau.
Một client luôn về cùng backend.
Một key/resource luôn về cùng cache node.
Round Robin chia lượt. Weighted Round Robin chia theo trọng số. Least Connections phản ứng với connection đang mở. IP Hash ưu tiên affinity, không ưu tiên fairness.
Comments
L4 và L7: phải biết trước khi chọn thuật toán
Layer 4
Load Balancer thấy TCP/UDP flow. Quyết định thường xảy ra khi connection được tạo. Một TCP connection dài có thể mang nhiều traffic nhưng vẫn chỉ được đếm là một connection.
Layer 7
Reverse proxy hiểu HTTP/gRPC và có thể route theo host, path, header, cookie hoặc từng request/stream.
HTTP/2 và gRPC multiplex nhiều stream trên một connection. Vì vậy:
“Least TCP connections” có thể không phản ánh số request đang chạy.
Một connection có 100 stream có thể nặng hơn 10 connection idle.
L7 least-active-requests, latency hoặc queue-aware routing có thể phù hợp hơn.
Luôn xác định Load Balancer đang đếm client connections, upstream connections, requests hay streams.
Bảng so sánh nhanh
1. Round Robin
Round Robin lần lượt chọn từng backend khỏe mạnh:
Khi phù hợp
Backend có capacity gần giống nhau.
Request duration/cost tương đối đồng đều.
Ứng dụng stateless.
Muốn thuật toán đơn giản, overhead thấp.
Pool đủ ổn định.
Nhược điểm
Round Robin cân bằng số lượt, không cân bằng công việc.
Mỗi backend nhận một request, nhưng tải hoàn toàn khác nhau.
TypeScript
Code minh họa tạo mảng healthy mỗi lần. Production sẽ giữ pool/index tối ưu, xử lý concurrent topology update và tránh race khi backend bị remove.
2. Weighted Round Robin
Weighted Round Robin dùng khi server không cùng năng lực.
Trong dài hạn, tỷ lệ mục tiêu gần 50% / 30% / 20%.
Use case
A có nhiều CPU/RAM hơn B.
Bare-metal và VM khác cấu hình.
Canary release nhận 5% traffic.
Capacity tạm thời bị giảm.
Multi-region có quota khác nhau.
Đừng đặt weight chỉ theo số CPU core
Throughput còn phụ thuộc:
CPU architecture/frequency.
Memory bandwidth.
Database pool.
GC behavior.
External API quota.
Request mix.
Version code.
Weight nên đến từ benchmark và telemetry, rồi rollout dần.
Smooth Weighted Round Robin
Nếu tạo danh sách [A,A,A,A,A,B,B,B,C,C], traffic có thể đến theo burst. Smooth Weighted Round Robin phân tán lựa chọn đều hơn.
Mỗi vòng:
Cộng effectiveWeight vào currentWeight của mọi node.
Chọn node có currentWeight lớn nhất.
Trừ tổng weight khỏi node được chọn.
Lookup này O(n) theo số backend. Ở pool rất lớn, có thể dùng cấu trúc hoặc thuật toán khác, nhưng thường network/proxy cost lớn hơn vòng lặp trên pool nhỏ.
3. Least Connections
Least Connections chọn backend có ít active connection nhất.
Khi connection được mở, counter tăng. Khi đóng, counter giảm.
Khi phù hợp
WebSocket, long polling, streaming.
Database proxy.
Request duration biến động mạnh.
Một connection chiếm resource đáng kể.
Weighted Least Connections
Với server khác capacity, so sánh normalized load:
Chọn score nhỏ nhất.
Tie-break nên dùng Round Robin hoặc random để tránh backend đầu danh sách luôn được chọn.
Vì sao Least Connections vẫn có thể sai?
Connection count chỉ là proxy:
Một connection idle và một connection upload 1GB đều tính là 1.
HTTP/2 connection có nhiều concurrent streams.
Một request CPU-heavy không phản ánh bằng connection count.
Connection pool từ proxy tới backend có thể tái sử dụng, khác số client connection.
Counter stale hoặc không giảm khi connection chết bất thường sẽ làm lệch quyết định.
Nếu có telemetry tốt, cân nhắc least requests, EWMA latency, queue depth hoặc adaptive load balancing.
4. IP Hash
IP Hash dùng hash của source IP để chọn backend:
Cùng IP và cùng topology thường về cùng backend.
TypeScript minh họa
Đây là modulo hash đơn giản. Khi thêm/xóa backend, nhiều client bị remap. Consistent hashing giảm remapping nhưng vẫn không sửa các vấn đề identity của IP.
IP Hash là affinity, không phải fairness
NAT và proxy
Hàng nghìn user trong một công ty, carrier NAT hoặc VPN có thể cùng source IP, nên tất cả vào một backend.
IP thay đổi
Mobile user đổi Wi-Fi/4G, IPv6 privacy address hoặc proxy egress có thể đổi IP, làm mất stickiness.
Real client IP
Nếu Load Balancer đứng sau CDN/proxy và hash $remote_addr, có thể đang hash IP của proxy. Chỉ tin X-Forwarded-For/PROXY protocol từ upstream được cấu hình là trusted.
Session state
IP Hash thường được dùng để “vá” ứng dụng giữ session trong RAM. Thiết kế tốt hơn:
Session store dùng Redis/database.
Stateless signed token khi phù hợp.
Cookie-based affinity nếu thật sự cần sticky session.
Graceful fallback khi backend mất.
Affinity không đồng nghĩa high availability. Backend chết thì session local vẫn có thể mất.
NGINX configuration
Round Robin mặc định
Weighted Round Robin
Least Connections
NGINX least_conn tính đến weight; khi hòa, weighted round robin được dùng để phân xử.
IP Hash
Passive failure parameters
Active health checks và một số tính năng nâng cao phụ thuộc edition/module. Luôn kiểm tra phiên bản NGINX đang chạy.
Health check quan trọng hơn thuật toán
Một Round Robin có health check tốt thường an toàn hơn thuật toán “thông minh” nhưng vẫn gửi traffic vào node lỗi.
Health check cần phân biệt:
Liveness: process còn sống.
Readiness: node sẵn sàng nhận traffic.
Dependency health: database/queue có đang usable.
Overload: node còn capacity hay nên load shed.
Không nên readiness check quá sâu đến mức một shared dependency lỗi khiến toàn bộ backend bị remove cùng lúc.
Cần:
Failure threshold.
Success threshold.
Timeout ngắn.
Interval có jitter.
Hysteresis để tránh flapping.
Outlier detection cho node có latency/error bất thường.
Slow start, draining và deployment
Backend mới healthy chưa chắc đã warm:
JIT chưa nóng.
Cache trống.
Connection pool chưa đầy.
Lazy initialization chưa xong.
Slow start tăng traffic dần thay vì cấp full weight ngay.
Khi deploy/scale-in:
Mark node not ready/draining.
Ngừng nhận connection/request mới.
Chờ in-flight request và WebSocket theo deadline.
Đóng connection có kiểm soát.
Remove khỏi pool.
Nếu xóa node ngay, retry và reconnect có thể tạo spike trên phần còn lại.
Keepalive, HTTP/2 và WebSocket
Keepalive
Load Balancer có thể giữ upstream connection pool. Số connection upstream không nhất thiết tương ứng số request/client.
HTTP/2/gRPC
Một connection chứa nhiều stream. L4 Round Robin chỉ phân phối lúc connection mở; một client tạo ít connection có thể dồn nhiều RPC vào một backend. Cần L7 proxy hoặc client-side balancing hiểu endpoint/stream.
WebSocket
Sau HTTP Upgrade, connection gắn với một backend đến khi đóng. Thuật toán chỉ ảnh hưởng lúc handshake. Least Connections thường có ý nghĩa hơn Round Robin nếu connection duration biến động, nhưng message rate trên từng socket vẫn có thể rất khác.
Weighted không tự động nghĩa dynamic
Weight cấu hình tĩnh không phản ứng ngay với:
CPU spike.
GC pause.
DB pool saturation.
Error rate tăng.
Zone network degradation.
Dynamic/adaptive balancing có thể dùng latency, errors hoặc queue, nhưng feedback loop phải ổn định. Thay weight quá nhanh dễ oscillation: traffic rời A sang B, B quá tải, rồi quay lại A.
Dùng smoothing, cooldown, min/max weight và rollout nhỏ.
Chọn thuật toán theo workload
Metrics phải đo
Theo từng backend:
Requests/second và new connections/second.
Active connections và active requests/streams.
Response time p50/p95/p99.
Error/timeout/reset rate.
CPU, memory, GC và event-loop lag.
Queue depth và thread/connection-pool saturation.
Bytes in/out.
Health state, ejection và recovery.
Actual traffic share so với configured weight.
Remap/session-affinity failure.
Không chỉ nhìn tổng cluster average; một node nóng có thể bị che bởi các node nhàn.
Test bằng mô phỏng và load test
3 backend cùng capacity, request bằng nhau.
Một backend chậm gấp 10 lần.
Backend có weight 5:3:2 đạt tỷ lệ dài hạn mong muốn.
Một node health flap.
Add/remove node trong IP Hash và đo remapping.
10.000 user sau cùng corporate NAT.
Mobile client đổi IP.
WebSocket có duration 1 giây đến 1 giờ.
HTTP/2 có 1 connection nhưng hàng trăm stream.
Rolling deploy với drain và slow start.
Load Balancer restart và state reset.
Multi-LB instance có state/counter độc lập.
Backend trả nhanh nhưng queue downstream đã đầy.
Zone failure và retry traffic chuyển sang zone còn lại.
Anti-patterns
Chọn Round Robin vì mặc định mà không đo request cost.
Đặt weight bằng CPU core rồi không benchmark.
Dùng Least Connections nhưng counter không phản ánh HTTP/2 streams.
Quên decrement counter khi disconnect/error.
Dùng IP Hash làm authentication hoặc identity.
Tin X-Forwarded-For từ Internet.
Dùng IP Hash với lượng lớn user sau NAT rồi kỳ vọng cân bằng.
Giữ session quan trọng chỉ trong RAM backend.
Gửi traffic vào node chưa warm.
Remove node mà không drain.
Health check quá nông hoặc quá sâu.
Chỉ nhìn request count, bỏ qua latency/error/bytes.
Retry không budget sau failover, làm node còn sống bị quá tải.
Production checklist
Xác định cân bằng theo connection, request hay stream.
Backend capacity và request cost đã được đo.
Thuật toán phù hợp HTTP/1.1, HTTP/2, gRPC/WebSocket.
Health check có threshold, timeout và hysteresis.
Backend mới có warm-up/slow start.
Scale-in/deploy dùng graceful drain.
Weight được validate bằng actual traffic và SLO.
Sticky session có fallback và không dựa vào IP nếu không cần.
Real client IP chỉ lấy từ trusted proxy chain.
Có outlier detection/load shedding/concurrency limit.
Dashboard hiển thị từng backend, không chỉ average.
Đã test NAT, topology change, long connections và zone failure.
Kết luận
Round Robin: lựa chọn mặc định tốt cho stateless workload đồng đều.
Weighted Round Robin: phù hợp khi capacity khác nhau hoặc cần chia traffic có chủ đích.
Least Connections: hữu ích cho connection/request dài và không đồng đều, miễn là connection count tương quan với tải.
IP Hash: tạo affinity đơn giản nhưng dễ lệch tải vì NAT, proxy và IP thay đổi.
Thuật toán chỉ là một phần. Load balancing production cần health checks, draining, slow start, trusted client IP, retry budget và observability theo từng backend.
1upstream app_backend {2 server 10.0.0.11:3000;3 server 10.0.0.12:3000;4 server 10.0.0.13:3000;5}
Codejavascript
1upstream app_backend {2 server 10.0.0.11:3000 weight=5;3 server 10.0.0.12:3000 weight=3;4 server 10.0.0.13:3000 weight=2;5}
Codejavascript
1upstream app_backend {2 least_conn;34 server 10.0.0.11:3000 weight=2;5 server 10.0.0.12:3000 weight=1;6}
Codejavascript
1upstream app_backend {2 ip_hash;34 server 10.0.0.11:3000;5 server 10.0.0.12:3000;6 server 10.0.0.13:3000;7}
Codejavascript
1upstream app_backend {2 least_conn;34 server 10.0.0.11:3000 max_fails=3 fail_timeout=30s;5 server 10.0.0.12:3000 max_fails=3 fail_timeout=30s;6 server 10.0.0.13:3000 backup;7}
Related posts
More articles with related topics and technologies.
Hướng dẫn HTTPS và TLS từ nền tảng đến production: rủi ro MITM của HTTP, chứng chỉ Let's Encrypt, Certbot với Nginx, ACME challenges, HSTS, renewal và troubleshooting.
Hướng dẫn tự động deploy ứng dụng Docker lên VPS Ubuntu và cloud providers bằng GitHub Actions: AWS ECS/App Runner/EC2, Vercel, Render, secrets, healthcheck, rollback và zero-downtime strategy.