Skip to content

🎯 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 CDNPull CDN
Cơ chếBạn chủ động upload content lên CDNCDN tự fetch từ origin khi có request đầu tiên
Kiểm soátToàn quyền: chọn file nào, PoP nàoCDN tự quyết định dựa trên traffic pattern
First requestNhanh — content đã sẵn sàngChậm — cold miss, phải fetch origin
Phù hợpContent ít thay đổi, file lớn (video, firmware)Website động, traffic không đều
Ví dụS3 + CloudFront với upload pipelineCloudflare proxy mode, Vercel CDN
Chi phí storageBạn trả storage cho toàn bộ contentCDN 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

StrategyCách hoạt độngƯu điểmNhược điểm
TTL-basedCache-Control: max-age=3600Đơn giản, zero configStale data trong TTL window
Versioned URLsapp.a3f8c2.js — hash trong filenameInvalidate tức thì, cache vĩnh viễnCần build pipeline hỗ trợ
Purge APIGọi API xóa cache khi content thay đổiChủ động, chính xácTốn thời gian propagate (~30s toàn cầu)
Stale-While-RevalidateServe stale, fetch fresh ở backgroundUX mượt, không blockingUser 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/HTML

Edge 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ắcDuy trì monitoring
85-95%TốtTối ưu TTL, kiểm tra cache key
70-85%Trung bìnhReview cache rules, thêm regional shield
< 70%KémKiế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ạnKhông CDNCó CDN (HIT)Có CDN (MISS)
DNS lookup50ms5ms (Anycast)5ms
TCP + TLS200ms (US)20ms (SG)20ms + 150ms
Server processing50ms0ms (cached)50ms
Data transfer200ms10ms10ms
Tổng500ms35ms235ms

✅ Checklist triển khai

Checklist ghi nhớ

Thiết kế

  • [ ] Static assets dùng versioned URLs + immutable cache header
  • [ ] HTML/API dùng stale-while-revalidate hoặc TTL ngắn + purge
  • [ ] Set Cache-Control: private cho mọi authenticated response
  • [ ] Thêm Vary header 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 -I từ 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à stale
  • stale-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.