Chuyển tới nội dung chính

Hiệu năng, cache, giới hạn và lỗi

Đọc trang này trước khi đưa trang web lên chạy thật.

Gọi từ trình duyệt (CORS)​

Mọi đường dẫn dưới /v2/public/cms/ trả về Access-Control-Allow-Origin: * cho mọi tên miền, nên trang web của bạn gọi thẳng từ trình duyệt được, không cần máy chủ trung gian.

Điều kiện:

  • Không gửi cookie hay thông tin đăng nhập: dùng credentials: 'omit' (hoặc mặc định của fetch khi khác tên miền). API không cho phép kèm thông tin đăng nhập.
  • Không thêm header nào (kể cả Authorization, Content-Type hay header X-… của bạn). Một yêu cầu GET không header là «simple request»: trình duyệt gửi thẳng, không cần yêu cầu OPTIONS trước. Thêm header là thêm một vòng mạng cho mỗi lần tải.
  • Chỉ các phương thức GET, HEAD và POST (cho tín hiệu lượt xem).
  • Các header giới hạn tần suất (x-ratelimit-*) không đọc được từ JavaScript khi gọi khác tên miền; chỉ mã trạng thái 429 là đọc được.

Bộ đệm (cache)​

Phản hồi thành công của ba đường dẫn GET có:

Cache-Control: public, s-maxage=60, max-age=0, must-revalidate
  • CDN giữ một bản cho mỗi URL tối đa 60 giây. Khi nhân sự lưu thay đổi, hệ thống chủ động xóa các URL bị ảnh hưởng khỏi CDN, nên thường thay đổi hiện ra ngay; 60 giây là mức trễ tối đa khi việc xóa không tới được.
  • Trình duyệt không dùng lại bản cũ mà luôn hỏi lại (CDN trả lời nhanh).
  • Phản hồi lỗi (4xx, 5xx) luôn có Cache-Control: private, no-store và không bao giờ được lưu.

Việc xóa chủ động chỉ biết tới một cách viết URL. Để trang của bạn nhận thay đổi ngay:

  • Viết tham số theo đúng thứ tự locale, category, tag, q, sort, page, limit, không thêm tham số khác.
  • Luôn ghi rõ locale, kể cả với /site.
  • Với danh sách, chỉ trang 1 được xóa chủ động; các trang sau, và mọi URL tìm kiếm (q), có thể trễ tới 60 giây.

Giữ đúng một cách viết URL cũng giúp mọi người đọc dùng chung một bản trong CDN.

Giới hạn tần suất​

Giới hạn tính theo địa chỉ của máy gọi:

Đường dẫnGiới hạn
GET /site, GET /entries, GET /entries/{entrySlug}20 yêu cầu / 10 giây, 50 yêu cầu / 10 giây (chung), 120 yêu cầu / 60 giây
POST /entries/{entrySlug}/view5 yêu cầu / 10 giây, 30 yêu cầu / 60 giây

Vượt giới hạn nhận 429 với errorCode: "RATE_LIMITED". Mỗi phản hồi có các header x-ratelimit-limit-*, x-ratelimit-remaining-*, x-ratelimit-reset-* (giây) cho các mức short, medium, long.

Với người đọc gọi từ trình duyệt, mỗi người có giới hạn riêng nên hiếm khi chạm tới. Rủi ro thật nằm ở máy chủ của bạn: khi dựng trang tĩnh, dựng trang phía máy chủ hay chạy một proxy, mọi yêu cầu đều đi từ một địa chỉ.

  • Khi dựng trang tĩnh: gọi tuần tự, khoảng 1 yêu cầu mỗi giây (dưới 120 yêu cầu/phút), dùng limit=100 để giảm số trang, và thử lại sau vài giây khi gặp 429.
  • Khi dựng phía máy chủ theo từng lượt truy cập: đặt bộ đệm của riêng bạn (ví dụ 60 giây) trước API. Không chuyển thẳng mỗi lượt truy cập thành một yêu cầu tới API.
async function getWithRetry(url: string, tries = 4): Promise<Response> {
for (let i = 0; ; i++) {
const res = await fetch(url, { credentials: 'omit' })
if (res.status !== 429 || i === tries - 1) return res
await new Promise((r) => setTimeout(r, 2000 * 2 ** i)) // 2 s, 4 s, 8 s
}
}

Xử lý lỗi​

Dạng thân lỗi và bảng mã: Tham chiếu API, Dạng lỗi. Danh sách cần làm:

  • 404 trên một entry → trang «Không tìm thấy» của bạn (với SSR, trả mã 404 thật). Với ngôn ngữ phụ, cân nhắc thử lại ở ngôn ngữ chính (xem).
  • 404 trên /site → mã trang web sai, trang web bị tắt, hoặc ngôn ngữ chưa bật. Đây là lỗi cấu hình, đừng hiện cho người đọc như một trang trống: dùng nội dung dự phòng và ghi log.
  • info: null → nhân sự chưa nhập thông tin; dùng tên, địa chỉ dự phòng mà bạn dựng sẵn.
  • 400 → lỗi trong mã của bạn (tham số sai, limit ngoài danh sách). Không thử lại.
  • 429, 5xx, lỗi mạng → thử lại có giãn cách; trong lúc chờ, giữ nội dung đang hiện hoặc hiện một khung «Không tải được, thử lại».
  • Danh sách rỗng (data: []) → hiện «Chưa có bài viết», nhất là với «Đọc nhiều» của một trang web mới.
  • Trường có thể thiếu → cover, excerpt, các khóa trong meta, các chuỗi trong info.
  • Kiểm tra dữ liệu nhận về trước khi dùng (kiểu của từng trường), và lọc lại bodyHtml trước khi chèn vào trang.
  • Tín hiệu lượt xem không bao giờ được làm hỏng trang: bỏ qua mọi lỗi.

Kiến trúc khuyến nghị​

Cách làmHợp vớiLưu ý
Gọi từ trình duyệt (SPA)Trang nhỏ, đội ít ngườiĐơn giản nhất; CORS đã mở. Nội dung hiện sau khi JavaScript chạy, kém hơn cho SEO.
Trang tĩnh (SSG) + gọi từ trình duyệt cho phần độngPhần lớn trang web giáo xứDựng sẵn vỏ trang, menu, thông tin liên hệ, các bài hiện có; tải danh sách mới nhất, tìm kiếm và «Đọc nhiều» từ trình duyệt. Dựng lại định kỳ (ví dụ mỗi giờ) để bài mới có trang tĩnh. Giữ một đường dự phòng phía trình duyệt cho slug chưa có lúc dựng.
Dựng phía máy chủ (SSR) có bộ đệmTrang lớn, cần SEO tốt cho mọi bàiĐặt bộ đệm 60 giây của riêng bạn, tôn trọng giới hạn tần suất, trả 404/301 thật cho slug không có/slug cũ.

Dù chọn cách nào:

  • Đặt địa chỉ gốc API và mã trang web vào biến cấu hình, không viết cứng rải rác.
  • Gom mọi lệnh fetch tới API vào một hàm (đúng credentials, không header, đúng thứ tự tham số).
  • Tín hiệu lượt xem chỉ gửi từ trình duyệt, không bao giờ lúc dựng trang.