Network: CDN và Load Balancer
10. CDN — Content Delivery Network
10.1 Vấn đề CDN giải quyết
Tốc độ ánh sáng là giới hạn cứng. Một user ở Việt Nam truy cập server ở Mỹ phải chịu RTT ~200-300ms — nửa giây chỉ cho vòng đi-về, chưa tính xử lý. CDN đặt các "điểm hiện diện" (PoP — Point of Presence) gần user để giảm khoảng cách vật lý.
Không có CDN:
User VN → Server US → RTT ~200-300ms mỗi request
Có CDN:
User VN → PoP Singapore → RTT ~10-20ms
(content serve từ gần nhất)
10.2 Kiến trúc CDN
CDN dùng Anycast routing: nhiều PoP trên thế giới announce cùng một IP prefix. BGP routing tự động đưa user đến PoP gần nhất theo network topology (không nhất thiết là gần nhất về địa lý).
Origin Server (US)
│
│ Pull khi cache miss
▼
Edge PoP Singapore ──── serve ────► User Vietnam
Edge PoP Tokyo ──────── serve ────► User Japan
Edge PoP Frankfurt ───── serve ────► User Germany
Khi user request một file, edge PoP kiểm tra cache. Nếu có (cache HIT) → serve ngay. Nếu không (cache MISS) → fetch từ Origin, cache lại, rồi serve.
10.3 CDN Caching Logic
Cache behavior được điều khiển bởi Cache-Control header trong response từ origin:
public max-age=86400 → Cache 1 ngày, CDN và browser đều cache được
private max-age=3600 → Chỉ browser cache, CDN không cache (user-specific data)
no-cache → Phải revalidate với origin trước khi dùng
no-store → Không cache bao giờ (sensitive data)
s-maxage=3600 → CDN cache 1h (override max-age cho CDN)
stale-while-revalidate → Trả cache cũ ngay, background refresh
ETag & Conditional Requests: Server gán ETag cho mỗi version của resource. Khi cache hết hạn, CDN gửi If-None-Match: "abc123" thay vì fetch lại toàn bộ. Nếu chưa thay đổi → server trả 304 Not Modified (không có body) → tiết kiệm bandwidth đáng kể.
10.4 CDN cho Dynamic Content
Static content (ảnh, CSS, JS, video) cache tốt vì ít thay đổi. Dynamic content (API responses, HTML personalized) khó hơn, nhưng vẫn có các kỹ thuật:
Edge Side Includes (ESI): tách trang thành phần có thể cache và phần không thể cache, CDN tự assemble:
<html>
<esi:include src="/header" ttl="3600"/> ← cached tại CDN
<div>User-specific content...</div> ← không cache
</html>
Stale-while-revalidate: trả response cũ ngay lập tức để latency thấp, đồng thời background fetch bản mới từ origin.
Vary header: cho CDN biết cache nhiều version khác nhau dựa trên request headers:
Vary: Accept-Encoding, Accept-Language
→ CDN cache version compressed/uncompressed, version vi/en riêng biệt
11. Load Balancer
11.1 Layer 4 vs Layer 7
Load balancer hoạt động ở hai tầng khác nhau với khả năng và trade-off khác nhau.
Layer 4 (Transport): Chỉ nhìn vào IP address và TCP/UDP port. Không hiểu nội dung HTTP. Cực nhanh vì chạy ở kernel space, thường dùng DPDK để bypass kernel hoàn toàn. Phù hợp cho: raw throughput cao, game servers, database connections.
Client ──── TCP connection ────► LB ──── TCP connection ────► Server
(LB forward TCP packets, không đọc content)
Layer 7 (Application): Đọc và hiểu HTTP headers, URL, cookies, thậm chí body. Tốn CPU hơn nhưng có thể làm nhiều thứ thông minh: route theo path, terminate TLS, inject headers, circuit breaking.
Client ──── HTTPS ────► LB (terminate TLS) ──── HTTP ────► Server
(LB đọc và xử lý HTTP — có thể route theo /api vs /static)
11.2 Load Balancing Algorithms
Mỗi thuật toán có trade-off khác nhau:
Round Robin: phân đều theo thứ tự. Đơn giản, tốt khi servers đồng đều nhau về capacity và request duration.
Weighted Round Robin: server mạnh hơn nhận nhiều traffic hơn theo tỉ lệ weight. Tốt khi servers có hardware khác nhau.
Least Connections: gửi đến server đang có ít active connections nhất. Tốt cho requests có duration không đều (long-polling, WebSocket).
Least Response Time: gửi đến server response nhanh nhất, đo liên tục. Adaptive nhất nhưng cần monitor overhead.
IP Hash / Consistent Hashing: cùng client IP luôn đến cùng server (sticky sessions). Consistent hashing dùng trong distributed cache — thêm/bớt server chỉ remap ~1/N keys thay vì toàn bộ.
11.3 Health Checks
LB phải biết server nào đang healthy để không route traffic đến server chết.
Active Health Check: LB chủ động gửi probe (thường là GET /health) mỗi vài giây. Server không trả 200 → remove khỏi pool ngay.
Passive Health Check: LB theo dõi traffic thực. Nếu X% requests failed → mark unhealthy. Nhẹ nhàng hơn nhưng phát hiện chậm hơn.
Circuit Breaker: pattern phổ biến ở layer service:
CLOSED → Traffic bình thường
│ Failure rate > threshold
▼
OPEN → Fast fail, không gửi request (bảo vệ server đang quá tải)
│ After timeout
▼
HALF-OPEN → Thử gửi 1 request
│ Success → CLOSED │ Failure → OPEN lại
11.4 Session Persistence (Sticky Sessions)
Vấn đề: user login vào Server A, request tiếp theo vào Server B → Server B không có session → bị logout.
Giải pháp 1 — Cookie-based sticky: LB set cookie SERVERID=server_a, mọi request tiếp theo route đến Server A. Vấn đề: server A chết → mất session.
Giải pháp 2 — Shared session store: tất cả servers đọc/ghi session vào Redis. LB có thể route bất kỳ đâu. Best practice cho stateless design.
Giải pháp 3 — JWT: session data nằm trong JWT token ở client. Server không lưu state gì cả → horizontally scalable hoàn toàn.
11.5 High Availability cho Load Balancer
LB là điểm hội tụ toàn bộ traffic — nếu nó chết thì hệ thống chết theo. Cần HA cho chính LB.
Single LB (SPOF):
Client → [LB] → [Server pool]
Active-Passive HA:
Client → [LB Primary] → [Server pool]
[LB Standby] ← chờ sẵn (VRRP/keepalived, cùng Virtual IP)
Khi primary chết → standby take over VIP trong vài giây
Active-Active HA:
DNS round-robin → [LB1] → [Server pool]
→ [LB2] → [Server pool]
Cả hai handle traffic → tăng throughput, không có downtime
Global Load Balancing (GSLB):
DNS-based routing đến LB gần nhất theo geography
→ Kết hợp với CDN cho production systems