Chương 02 · Entry Layer

Load Balancer & Reverse Proxy

L4 vs L7, các thuật toán phân tải, health check, sticky session, nginx/HAProxy/cloud LB, và Global Load Balancing.

1. Load Balancer là gì?

Load Balancer (LB) phân phối incoming traffic giữa nhiều server backend. Component này là trái tim của horizontal scaling — không có LB, scale ngang không khả thi.

5 chức năng chính:

  1. Distribute load — chia request giữa các backend.
  2. Health check — phát hiện backend chết, không gửi traffic đến.
  3. SSL termination — handle TLS, backend nhận HTTP plain.
  4. Failover — khi backend chết, chuyển traffic sang backend khác trong giây.
  5. Smart routing — route theo URL, header, geo, A/B test.

1.1. Vị trí trong kiến trúc

Client / User │ ▼ ┌──────────────┐ │ DNS │ ← global routing (latency, geo) └──────┬───────┘ │ ▼ ┌──────────────┐ │ CDN edge │ ← cache static, SSL termination └──────┬───────┘ │ ▼ ┌──────────────┐ │ Load Balancer│ ← phân phối tải đến backend │ (L4 or L7) │ └──────┬───────┘ │ ┌──────┴──────┬──────────┬──────────┐ ▼ ▼ ▼ ▼ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │Backend1│ │Backend2│ │Backend3│ │Backend4│ └────────┘ └────────┘ └────────┘ └────────┘

2. L4 vs L7 — hai tầng phân tải

"L4" và "L7" tham chiếu OSI layers (đã học ở Networking):

L4 Load Balancer (Transport)

Phân tải dựa trên thông tin TCP/UDP — IP và port nguồn/đích. KHÔNG hiểu HTTP.

  • Ưu: cực nhanh, ít CPU, latency thấp; xử lý mọi protocol (HTTP, gRPC, WebSocket, custom TCP, UDP).
  • Nhược: không thể route theo URL/header; không SSL termination tự nhiên (chỉ pass-through).
  • Ví dụ: AWS NLB, HAProxy mode TCP, nginx stream module.
  • Khi dùng: traffic non-HTTP (database proxy, gaming server, MQ); cần latency tối thiểu.

L7 Load Balancer (Application)

Phân tải dựa trên HTTP — URL path, header, cookie, body. Hiểu HTTP/HTTPS/HTTP2/gRPC.

  • Ưu: smart routing (path, host, header), SSL termination, response caching, content modification, request retry, websocket upgrade.
  • Nhược: chậm hơn L4 (parse HTTP), tốn CPU hơn.
  • Ví dụ: AWS ALB, nginx, HAProxy mode HTTP, Envoy, Traefik, Kong.
  • Khi dùng: web app, API; cần routing thông minh.

2.1. Có thể dùng cả hai

Pattern phổ biến: L4 ở edge → L7 phía sau. NLB nhận TLS terminate ở ALB phía sau cho phân tải HTTP-aware.

2.2. SSL termination

  Client ──HTTPS──► [LB] ──HTTP──► Backend
                     ▲
            SSL terminate ở đây
            (LB cầm cert, decrypt, forward)

Lợi: backend không tốn CPU encrypt/decrypt; quản lý cert ở 1 chỗ. Bảo mật: traffic LB→backend nội bộ thường an toàn (private network), nếu cần extra: SSL re-encryption (mTLS) tại LB.

3. Thuật toán phân tải

3.1. Round Robin

Quay vòng đều: req1 → server A, req2 → B, req3 → C, req4 → A...

Ưu: đơn giản, fair khi server đồng nhất.

Nhược: bỏ qua load thực tế. Server đang xử lý request "nặng" vẫn nhận thêm.

3.2. Weighted Round Robin

Mỗi server có weight, chia tỉ lệ. Server A weight 3, B weight 1 → A nhận 75% traffic.

Khi dùng: server có cấu hình khác nhau (mix giữa generation hardware), canary deploy.

3.3. Least Connections

Chọn server đang có ít active connection nhất.

Ưu: phản ánh load thực tế tốt hơn round robin.

Phù hợp: long-lived connection (WebSocket, long polling, streaming).

3.4. Least Response Time

Chọn server có latency average thấp nhất + ít connection.

Phức tạp hơn least conn; HAProxy có sẵn.

3.5. IP Hash / Source IP Hashing

Hash IP client → cùng IP luôn route đến cùng server. Dùng cho sticky session đơn giản.

Vấn đề: nhiều user share IP (NAT corporate, mobile carrier) → uneven distribution.

3.6. Consistent Hashing

Đã học ở Database (Ch10). Hash key (IP / userId / cache key) lên vòng tròn, mỗi server "sở hữu" 1 đoạn cung. Thêm/bớt server → chỉ ~1/N traffic redistribute.

Dùng cho: cache cluster (CloudFront, Memcached cluster), sharded service.

3.7. Random + Power of Two Choices

Pick 2 server random, chọn cái load thấp hơn. Đơn giản, gần optimal:

function pickBackend(backends: Backend[]) {
  const a = backends[Math.floor(Math.random() * backends.length)];
  const b = backends[Math.floor(Math.random() * backends.length)];
  return a.activeConnections < b.activeConnections ? a : b;
}

Performance: với power-of-two-choices, max load expected là log(log(N)) thay vì log(N) như round robin. Used trong haproxy-2-choice mode, NetflixZuul.

3.8. So sánh thuật toán

AlgorithmKhi tốtHạn chế
Round RobinServer đồng nhất, request nhẹ tương đương nhauBỏ qua load thực
Weighted RRServer khác cấu hình, canaryVẫn không nhìn load thực
Least ConnectionsWebSocket, long polling, streamingCần track state global
IP HashSticky đơn giảnNAT skew distribution
Consistent HashCache cluster, sharded servicePhức tạp implement
P2C RandomDistributed, không cần state globalRandom nhưng đủ tốt
Default thực dụng Mặc định: Round Robin hoặc Least Connections. 90% case đủ. Chỉ chọn khác khi có lý do cụ thể (consistent hash cho cache; weighted cho canary).

4. Health Check — phát hiện server chết

LB liên tục ping backend. Backend không response → đánh dấu unhealthy → ngừng route traffic.

4.1. Active vs Passive

  • Active health check — LB chủ động ping định kỳ (mỗi 5-30s) đến endpoint /health. Phổ biến nhất.
  • Passive health check — LB monitor real traffic, mark unhealthy khi error rate cao. Tiết kiệm tài nguyên hơn.

4.2. Endpoint /health design

// Liveness — chỉ check process còn sống
app.get('/health/live', (req, res) => {
  res.status(200).json({ status: 'ok' });
});

// Readiness — sẵn sàng nhận traffic? (DB connect OK, cache warm)
app.get('/health/ready', async (req, res) => {
  try {
    await db.query('SELECT 1');
    await redis.ping();
    res.status(200).json({ status: 'ready' });
  } catch (e) {
    res.status(503).json({ status: 'not ready', error: e.message });
  }
});

Phân biệt:

  • Liveness — process có chạy không? Fail → restart (Kubernetes).
  • Readiness — sẵn sàng nhận traffic? Fail → tạm thời không gửi traffic, KHÔNG restart.

4.3. Threshold

Tránh "flapping" (lúc up lúc down):

  • Mark unhealthy: cần 2-3 fail liên tiếp.
  • Mark healthy lại: cần 2-3 success liên tiếp.

4.4. Cascading failure

Vấn đề: khi 1 server unhealthy, LB chuyển traffic sang server còn lại → server còn lại quá tải → cũng unhealthy → toàn bộ chết.

Phòng tránh:

  • Capacity dư — vận hành dưới 80% (đã học Ch1).
  • Circuit breaker — cắt traffic đến server problematic chứ không "xóa".
  • Slow start — server mới khôi phục nhận traffic từ từ, không 100% ngay.

5. Sticky Session

Pattern: cùng user luôn route đến cùng backend.

5.1. Cách triển khai

  • Cookie-based — LB set cookie chứa server ID. Request kế tiếp có cookie → route đến đúng server.
  • IP Hash — đã nói ở 3.5.

5.2. Khi nào cần?

Hiện đại: tránh sticky session nếu có thể. Pattern stateless tốt hơn nhiều:

  • Session ở Redis/DB shared.
  • Ai vào đâu cũng được — cluster horizontal scale tự nhiên.

Sticky chỉ cần khi:

  • Migrate legacy app stateful.
  • WebSocket — connection lâu, switch server giữa chừng phức tạp.
  • In-memory state cho specific session (live game state).

5.3. Vấn đề sticky session

  • Uneven load — user heavy hit cùng server.
  • Server chết = session chết — fall back đến server khác mất state.
  • Khó scale — auto-scale không hiệu quả nếu mỗi user "neo" vào 1 server.

Thực dụng: dùng JWT (stateless) cho auth → không cần sticky cho HTTP nhúng. Cho WebSocket: connection-pinning OK vì đã established.

6. Reverse Proxy vs Load Balancer

Hai khái niệm thường nhầm lẫn:

Load BalancerReverse Proxy
Định nghĩaPhân phối traffic giữa nhiều backendĐứng trước backend, nhận request thay backend
Yêu cầu nhiều backend?Có (≥ 2)Không (có thể 1)
Chức năngDistribute, health check, failoverCaching, SSL termination, compression, request modification, security
Ví dụAWS ALB/NLB, F5nginx, Varnish, CloudFlare
Mối quan hệnginx có thể là CẢ HAI — reverse proxy + load balancer

Trong thực tế: nginx/Envoy thường vừa làm reverse proxy vừa làm L7 load balancer. Cùng 1 instance, cùng config.

6.1. Forward Proxy vs Reverse Proxy

  • Forward Proxy — đứng trước CLIENT, ẩn client với server (như corporate proxy, VPN).
  • Reverse Proxy — đứng trước SERVER, ẩn server với client.
FORWARD: Client ──► [Forward Proxy] ──► Server (ẩn client với server) REVERSE: Client ──► [Reverse Proxy] ──► Server (ẩn server với client)

7. nginx, HAProxy, Envoy — công cụ phổ biến

7.1. nginx

Reverse proxy + L7 LB phổ biến nhất. Event-driven (master/worker), C, cực nhanh.

# /etc/nginx/nginx.conf — basic LB setup
upstream backend {
    least_conn;                                # algorithm
    server backend1.example.com:8080 weight=3;
    server backend2.example.com:8080 weight=1;
    server backend3.example.com:8080 backup;   # chỉ dùng khi cả 2 trên fail
}

server {
    listen 443 ssl http2;
    server_name api.example.com;

    ssl_certificate     /etc/ssl/cert.pem;
    ssl_certificate_key /etc/ssl/key.pem;

    location / {
        proxy_pass http://backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_connect_timeout 5s;
        proxy_read_timeout 30s;
    }

    location /static {
        proxy_pass http://backend;
        proxy_cache static_cache;
        proxy_cache_valid 200 1d;            # cache response 200 trong 1 ngày
        add_header X-Cache-Status $upstream_cache_status;
    }

    location /health {
        return 200 "OK";
        access_log off;
    }
}

7.2. HAProxy

L4 + L7 LB, từ 2001. Đặc biệt mạnh cho TCP balancing, statistics phong phú.

frontend http-in
    bind *:80
    default_backend webservers

backend webservers
    balance leastconn
    option httpchk GET /health
    server web1 10.0.0.1:8080 check inter 2000 rise 2 fall 3
    server web2 10.0.0.2:8080 check inter 2000 rise 2 fall 3
    server web3 10.0.0.3:8080 check inter 2000 rise 2 fall 3 weight 50

7.3. Envoy / Istio

Envoy = Lyft 2016, được CNCF. Modern data plane: gRPC native, observability tốt, hot reload không downtime.

Istio dùng Envoy làm sidecar trong service mesh — sẽ học Ch6.

7.4. Cloud LB

  • AWS ELB — Classic LB (deprecated), ALB (L7), NLB (L4 ultra-fast), GLB (Global Accelerator).
  • GCP — Cloud Load Balancer (global anycast).
  • Cloudflare — Load Balancing trên edge.
  • F5 / Citrix NetScaler — hardware enterprise, đắt.

Cloud managed LB tự autoscale, multi-AZ HA built-in. Trừ khi có lý do đặc biệt, dùng managed cho tiết kiệm vận hành.

8. Global Load Balancing

Load Balancer thông thường = trong 1 region. Khi user toàn cầu, cần Global LB để route đến region gần nhất.

8.1. DNS-based

Cách đơn giản: DNS resolve api.example.com trả IP khác nhau theo geo.

  • User VN → IP của data center Singapore.
  • User EU → IP của data center Frankfurt.
  • User US → IP của data center Virginia.

Tools: AWS Route 53 (geolocation routing), Cloudflare Load Balancing (DNS-based).

Vấn đề: TTL DNS gây lag — failover region mất thời gian (TTL thường 5min).

8.2. Anycast

Cùng 1 IP công khai, nhưng được announce từ nhiều địa điểm địa lý qua BGP. Internet tự route đến địa điểm gần nhất.

Cloudflare, Google, AWS Global Accelerator dùng Anycast. Failover dưới giây — khi 1 region down, BGP tự withdraw route, traffic chuyển đến region khác.

8.3. CDN — Global LB cho static

CDN (CloudFront, Cloudflare, Fastly) là Global LB cho content cacheable. Sẽ chi tiết Ch7.

9. Patterns thực tế

9.1. Multi-tier LB

Internet │ ▼ ┌─────────────────────┐ │ Cloudflare (Anycast)│ Global routing, DDoS protection, CDN └──────────┬──────────┘ │ ▼ ┌─────────────────────┐ │ AWS NLB (L4) │ TLS pass-through, ultra-fast └──────────┬──────────┘ │ ▼ ┌─────────────────────┐ │ nginx (L7) │ Smart routing, SSL termination, caching └──────────┬──────────┘ │ ┌─────┴─────┐ ▼ ▼ ┌──────┐ ┌──────┐ │ App │ │ App │ └──────┘ └──────┘

Mỗi tầng giải 1 vấn đề: edge (CDN/DDoS) → ingress (TLS, fast) → app routing (smart) → backend.

9.2. Blue-Green Deployment

2 environment giống nhau:

  • Blue — production hiện tại.
  • Green — version mới deploy.

Test green xong, switch LB từ blue → green. Sự cố? Switch back ngay.

9.3. Canary Deployment

Route 5% traffic đến version mới, 95% version cũ. Theo dõi metrics. OK → tăng dần đến 100%.

# Canary với weight:
upstream app {
    server v1.app.local weight=95;
    server v2.app.local weight=5;
}

9.4. A/B Testing

Tương tự canary nhưng split theo user (cookie/userId hash) để cùng user luôn thấy version giống nhau:

map $cookie_user_id $backend_version {
    default          v1;
    "~*[0-9a-f]$"    v2;   # user_id ending in 0-9a-f → v2 (50%)
}

9.5. Geographic routing với fallback

Route theo geo, nhưng nếu region gần down → fallback sang region khác:

  • Primary: VN user → ap-southeast-1.
  • Fallback: ap-southeast-1 down → ap-northeast-1.

AWS Route 53 health check + geo routing làm tự động.

10. Bài tập

  1. So sánh L4 vs L7: cho 3 use case, mỗi cái nói nên dùng L4 hay L7 và lý do.
    • (a) Database proxy cho 100 connection postgres
    • (b) Web app với routing /api/v1, /api/v2 đến backend khác
    • (c) Game server real-time UDP
  2. Setup nginx local làm reverse proxy + LB cho 3 Node.js app instance. Test với ab hoặc k6.
  3. Implement health check cho 1 service: /health/live + /health/ready. Trong ready: check DB, Redis. Đảm bảo trả 503 khi DB down.
  4. Phân biệt thuật toán cho 3 scenario:
    • WebSocket app (connection lâu).
    • Cache cluster với 4 node Redis.
    • Stateless API CRUD bình thường.
  5. Setup Cloudflare DNS với load balancing: 2 region (US, EU), health check tự động fail over.
  6. Implement canary deployment với nginx: 5% traffic v2, 95% v1. Sau 1h mà không có error rate tăng → bump v2 lên 25%.
  7. Vì sao nên tránh sticky session khi có thể? Cho 2 alternative cụ thể.

11. Quiz

Quiz cuối Chương 2

L4 vs L7 Load Balancer khác chính ở:

  • L4 chậm hơn
  • L4 phân tải dựa IP/port (transport layer); L7 hiểu HTTP — route theo URL/header/cookie, SSL termination, caching
  • L7 chỉ cho TCP
  • L4 không có SSL
L4 cực nhanh, không hiểu HTTP. L7 chậm hơn nhưng smart: route /api/v1 đến backend1, /api/v2 đến backend2; cache static; SSL terminate. Pattern phổ biến: NLB (L4) + ALB/nginx (L7) phía sau.

Least Connections phù hợp NHẤT cho:

  • Server đồng nhất, request CPU-bound
  • CDN edge
  • WebSocket / long polling — connection lâu, đếm conn phản ánh load thực tốt hơn round robin
  • Static asset
WebSocket connection mở 30 phút - vài giờ. Round robin → server vào sau nhận ít connection nhưng vẫn được round robin → uneven. Least conn nhìn vào connection thực, distribute đều hơn.

Liveness probe vs Readiness probe:

  • Cùng nghĩa
  • Cả hai dùng cho load balancing
  • Liveness check connection, Readiness check disk
  • Liveness: process còn chạy không (fail → restart). Readiness: sẵn sàng nhận traffic không (fail → tạm cách ly khỏi LB, không restart)
Liveness fail → kill và restart container (Kubernetes). Readiness fail → tạm xóa khỏi LB pool nhưng không restart (vd: warming cache, đang reconnect DB). Cùng app nhưng 2 endpoint khác.

Sticky session nên TRÁNH khi có thể vì:

  • Uneven load, server chết = mất session, khó autoscale; nên lưu session ở Redis/DB shared
  • Quá nhanh
  • Tốn ít RAM
  • Cần Java
Sticky "neo" user vào server cụ thể → user heavy đẩy server đó quá tải; server crash → user mất session; autoscale không hiệu quả vì không thể đơn giản tăng node. Stateless với JWT hoặc session ở Redis là cách tốt hơn.

Consistent hashing thường dùng cho:

  • Sticky session HTTP
  • Round robin
  • Cache cluster (memcached/Redis sharded) — thêm/bớt node chỉ ~1/N traffic redistribute
  • Health check
Cache cluster với hash(key) % N → đổi N → 90% key remap → cache miss storm. Consistent hashing với vòng tròn → chỉ 1/N key đổi node. Cassandra, DynamoDB, Memcached client lib đều dùng pattern này.

Anycast routing:

  • Cùng tên, IP khác mỗi region
  • Cùng IP công khai announce từ nhiều địa điểm qua BGP, Internet tự route đến nơi gần nhất; failover dưới giây
  • Multi-cast UDP
  • VPN
Cloudflare, Google DNS (8.8.8.8), AWS Global Accelerator dùng Anycast. Một IP, nhiều POP. Khi 1 POP down, BGP withdraw route, traffic flow đến POP khác — không cần thay đổi DNS, không có TTL lag.

Cascading failure trong LB cluster:

  • LB chậm
  • Sticky session crash
  • DNS sai
  • 1 server unhealthy → traffic chuyển sang server còn lại → quá tải → cũng unhealthy → toàn cụm chết. Phòng tránh: capacity dư, circuit breaker, slow start
Cluster 3 node 60% utilization. 1 node chết → traffic 60% × 1.5 = 90% mỗi node còn lại. Spike đơn giản → quá tải, crash dây chuyền. Vận hành dưới 80% capacity, slow start cho server mới recover, circuit breaker cắt traffic problem service.

Reverse Proxy vs Load Balancer:

  • LB phân tải nhiều backend; Reverse Proxy đứng trước backend (có thể 1) làm caching/SSL/security; nginx/Envoy làm cả 2
  • Hai cái giống hệt
  • Reverse Proxy chỉ cho web
  • LB không có SSL
LB cần ≥ 2 backend, focus distribute. Reverse Proxy có thể 1 backend, focus caching/SSL/security/compression/header modification. Trong thực tế, nginx/Envoy đảm nhiệm cả 2 vai trò trong 1 instance.

Hoàn thành Chương 2. Tiếp theo: Chương 3 — Caching Strategies →