Giao diện
🎯 Mục tiêu
🎯 Sau bài này bạn sẽ nắm được:
- Kiến trúc CDN: PoP, edge server, origin shield — luồng request từ user đến origin
- Push CDN vs Pull CDN — trade-off và khi nào dùng cái nào
- 4 chiến lược cache invalidation: TTL, versioned URL, purge API, stale-while-revalidate
- Edge functions — chạy logic ngay tại điểm gần user nhất
- Multi-tier caching: Browser → CDN PoP → Regional Shield → Origin
CDN & Edge Computing — Đưa dữ liệu đến gần người dùng
Người dùng ở Việt Nam truy cập server đặt tại US-East — latency 200ms+ cho mỗi request. Một trang web trung bình cần 50 requests (HTML, CSS, JS, hình ảnh, fonts, API calls). Nhân lên, tổng thời gian tải vượt 10 giây. Quá nửa người dùng mobile rời trang sau 3 giây. Bạn không chậm vì code tệ — bạn chậm vì vật lý: ánh sáng trong cáp quang mất ~67ms để đi từ Hồ Chí Minh đến Virginia rồi quay về.
CDN (Content Delivery Network) giải quyết bài toán này bằng cách đặt bản sao dữ liệu tại hàng trăm điểm trên toàn cầu. Kết quả: latency giảm từ 200ms xuống 15-30ms, bandwidth tiết kiệm 60-80%, origin server được giảm tải đáng kể, và bạn có thêm lớp phòng thủ DDoS miễn phí. Edge computing đẩy khái niệm này xa hơn — không chỉ cache dữ liệu, mà chạy code ngay tại edge.
Bức tranh tư duy
Hãy nghĩ CDN như mạng lưới cửa hàng tiện lợi. Thay vì mỗi lần cần chai nước phải chạy về kho trung tâm ở ngoại thành (origin server), hàng hóa phổ biến được phân phối sẵn ở cửa hàng mỗi khu phố (PoP — Point of Presence). Bạn chỉ cần đi bộ 2 phút thay vì lái xe 30 phút. Kho trung tâm vẫn tồn tại cho những mặt hàng hiếm hoặc khi cửa hàng cần nhập thêm.
Khi user ở Hồ Chí Minh request ảnh avatar, request đi tới PoP gần nhất (Singapore, ~15ms). Nếu PoP đã cache → trả về ngay. Nếu chưa → hỏi regional shield (tầng cache trung gian). Chỉ khi cả hai miss, request mới phải vượt Thái Bình Dương về origin.
Cốt lõi kỹ thuật
Push CDN vs Pull CDN
| Tiêu chí | Push CDN | Pull CDN |
|---|---|---|
| Cơ chế | Bạn chủ động upload content lên CDN | CDN tự fetch từ origin khi có request đầu tiên |
| Kiểm soát | Toàn quyền: chọn file nào, PoP nào | CDN tự quyết định dựa trên traffic pattern |
| First request | Nhanh — content đã sẵn sàng | Chậm — cold miss, phải fetch origin |
| Phù hợp | Content ít thay đổi, file lớn (video, firmware) | Website động, traffic không đều |
| Ví dụ | S3 + CloudFront với upload pipeline | Cloudflare proxy mode, Vercel CDN |
| Chi phí storage | Bạn trả storage cho toàn bộ content | CDN chỉ cache content được request |
Thực tế phổ biến: Hầu hết production system dùng Pull CDN vì đơn giản hơn — CDN tự quản lý lifecycle. Push CDN chỉ dùng khi bạn cần control tuyệt đối (video streaming, firmware update).
Cache Invalidation Strategies
| Strategy | Cách hoạt động | Ưu điểm | Nhược điểm |
|---|---|---|---|
| TTL-based | Cache-Control: max-age=3600 | Đơn giản, zero config | Stale data trong TTL window |
| Versioned URLs | app.a3f8c2.js — hash trong filename | Invalidate tức thì, cache vĩnh viễn | Cần build pipeline hỗ trợ |
| Purge API | Gọi API xóa cache khi content thay đổi | Chủ động, chính xác | Tốn thời gian propagate (~30s toàn cầu) |
| Stale-While-Revalidate | Serve stale, fetch fresh ở background | UX mượt, không blocking | User có thể thấy data cũ 1 lần |
Best practice trong production: Kết hợp versioned URLs cho static assets (cache 1 năm) + stale-while-revalidate cho API responses (fresh nhưng không blocking).
# Cloudflare Cache-Control header thực tế
Cache-Control: public, max-age=31536000, immutable # Static assets (hashed filename)
Cache-Control: public, max-age=0, s-maxage=60, stale-while-revalidate=300 # API/HTMLEdge Functions / Workers
Edge functions cho phép bạn chạy logic ngay tại PoP, không cần round-trip về origin. Use cases phổ biến: A/B testing, geo-routing, authentication check, request rewriting, personalization.
javascript
// Cloudflare Worker — Geo-routing thực tế
export default {
async fetch(request, env) {
const country = request.cf?.country || 'US';
const url = new URL(request.url);
// Redirect user Việt Nam sang subdomain tiếng Việt
if (country === 'VN' && !url.hostname.startsWith('vn.')) {
url.hostname = `vn.${url.hostname}`;
return Response.redirect(url.toString(), 302);
}
// A/B testing dựa trên cookie
const cookie = request.headers.get('Cookie') || '';
const variant = cookie.includes('ab=B') ? 'B' : 'A';
const response = await fetch(request);
const newResponse = new Response(response.body, response);
newResponse.headers.set('X-Variant', variant);
return newResponse;
}
};Multi-tier Caching
Thiết kế đúng multi-tier caching, chỉ 1-5% requests đến origin. Mỗi tầng lọc bớt traffic, bảo vệ tầng phía sau.
Thực chiến
Ví dụ 1: Static asset CDN với versioned URLs
yaml
# vercel.json — Cache rules cho static assets
{
"headers": [
{
"source": "/assets/(.*)",
"headers": [
{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
]
},
{
"source": "/(.*).html",
"headers": [
{ "key": "Cache-Control", "value": "public, max-age=0, must-revalidate" }
]
}
]
}Nguyên tắc: HTML luôn revalidate (để user nhận link tới asset mới), static assets cache vĩnh viễn (vì filename chứa hash — đổi content = đổi URL).
Ví dụ 2: Edge middleware cho personalization
javascript
// Vercel Edge Middleware — middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const country = request.geo?.country || 'US';
const response = NextResponse.next();
// Thêm header để origin biết geo của user
response.headers.set('X-User-Country', country);
// Block traffic từ country bị restricted
const blocked = ['KP', 'IR', 'SY'];
if (blocked.includes(country)) {
return new NextResponse('Access Denied', { status: 403 });
}
return response;
}
export const config = { matcher: ['/api/:path*', '/dashboard/:path*'] };⚠️ Cạm bẫy
Sai lầm điển hình
❌ 1. Cache authenticated content trên CDN Response chứa thông tin cá nhân (profile, billing) bị cache → user khác nhận được data không phải của mình. Luôn set Cache-Control: private hoặc no-store cho authenticated endpoints.
❌ 2. TTL quá dài, không có purge strategy Set max-age=86400 cho API response → deploy hotfix nhưng user vẫn thấy bug cũ suốt 24 giờ. Dùng versioned URLs cho static, TTL ngắn + stale-while-revalidate cho dynamic.
❌ 3. Thiếu Vary header — serve sai content CDN cache response cho desktop, serve lại cho mobile user. Thêm Vary: Accept-Encoding, User-Agent (hoặc tốt hơn: Vary: Accept-Encoding + tách URL cho mobile).
❌ 4. Chạy logic nặng trong edge function Edge function có giới hạn CPU time (Cloudflare Workers: 10-50ms CPU). Đặt database query, image processing ở edge → timeout. Edge chỉ nên xử lý routing, auth check, header manipulation — logic nặng thuộc về origin.
Under the Hood
Cache Hit Ratio — Con số sống còn
Cache hit ratio = Requests served from cache / Total requests × 100%
| Hit Ratio | Đánh giá | Hành động |
|---|---|---|
| > 95% | Xuất sắc | Duy trì monitoring |
| 85-95% | Tốt | Tối ưu TTL, kiểm tra cache key |
| 70-85% | Trung bình | Review cache rules, thêm regional shield |
| < 70% | Kém | Kiến trúc có vấn đề — audit ngay |
Cost analysis
Giả sử origin xử lý 1 request tốn $0.001 (compute + bandwidth). Với 10 triệu requests/ngày:
- Không CDN: 10M × $0.001 = $10,000/ngày
- CDN hit ratio 95%: Origin chỉ nhận 500K requests → $500 + CDN fee ~$200 = $700/ngày
- Tiết kiệm: ~93% chi phí infrastructure
Latency breakdown
| Giai đoạn | Không CDN | Có CDN (HIT) | Có CDN (MISS) |
|---|---|---|---|
| DNS lookup | 50ms | 5ms (Anycast) | 5ms |
| TCP + TLS | 200ms (US) | 20ms (SG) | 20ms + 150ms |
| Server processing | 50ms | 0ms (cached) | 50ms |
| Data transfer | 200ms | 10ms | 10ms |
| Tổng | 500ms | 35ms | 235ms |
✅ Checklist triển khai
Checklist ghi nhớ
Thiết kế
- [ ] Static assets dùng versioned URLs +
immutablecache header - [ ] HTML/API dùng
stale-while-revalidatehoặc TTL ngắn + purge - [ ] Set
Cache-Control: privatecho mọi authenticated response - [ ] Thêm
Varyheader khi response phụ thuộc vào request header
Vận hành
- [ ] Monitor cache hit ratio — alert khi dưới 85%
- [ ] Có purge strategy rõ ràng cho mỗi loại content
- [ ] Test cache behavior từ nhiều region (dùng
curl -Itừ VPN/cloud shell) - [ ] Đặt regional shield để giảm origin load khi cache miss đồng loạt
Edge Functions
- [ ] Giới hạn logic ở edge: routing, auth check, header rewrite
- [ ] Monitor cold start time và CPU usage của edge functions
Bài tập luyện tập
🧠 Quiz
Câu 1: Push vs Pull CDN
Bạn đang xây hệ thống phân phối firmware update (file 500MB, thay đổi mỗi tháng, cần available ở mọi region ngay khi release). Nên dùng CDN model nào?
- [ ] Pull CDN — để CDN tự fetch khi user request
- [x] Push CDN — chủ động upload lên tất cả PoP trước khi announce release
- [ ] Không cần CDN — serve trực tiếp từ origin
- [ ] Pull CDN với TTL dài
Giải thích
Push CDN là lựa chọn đúng vì:
- File lớn (500MB) — nếu dùng Pull, user đầu tiên ở mỗi region phải chờ fetch từ origin → trải nghiệm tệ
- Release có lịch rõ ràng → bạn biết chính xác khi nào cần push
- Cần available ngay tại mọi region → Push đảm bảo content sẵn sàng trước khi user biết
Pull CDN phù hợp hơn cho content nhỏ, traffic không đoán trước, thay đổi thường xuyên.
🧠 Quiz
Câu 2: Cache Invalidation
API endpoint /api/products trả về danh sách sản phẩm. Data thay đổi ~10 lần/ngày. Bạn cần user thấy data mới trong vòng 5 phút nhưng không muốn mỗi request đều hit origin. Cấu hình Cache-Control nào phù hợp nhất?
- [ ]
Cache-Control: no-cache, no-store - [ ]
Cache-Control: public, max-age=86400 - [x]
Cache-Control: public, s-maxage=60, stale-while-revalidate=300 - [ ]
Cache-Control: private, max-age=300
Giải thích
s-maxage=60, stale-while-revalidate=300 là lựa chọn tối ưu:
s-maxage=60: CDN cache trong 60 giây, sau đó coi là stalestale-while-revalidate=300: Trong 5 phút tiếp theo, CDN serve bản stale ngay lập tức đồng thời fetch bản mới từ origin ở background- User luôn nhận response nhanh (từ cache), data mới nhất available sau tối đa ~60 giây
no-cache, no-store → mọi request đều hit origin (quá tốn kém). max-age=86400 → data cũ 24 giờ. private → CDN không cache được.