Cloudflare Blog
75

Thủ thuật

cdnjs chính thức chuyển mình lên nền tảng Cloudflare Developer

(giờ Việt Nam)

Tóm tắt AI

Kể từ 23/6/2026, dịch vụ CDN mã nguồn mở cdnjs đã vận hành hoàn toàn trên nền tảng Cloudflare, hỗ trợ đắc lực cho các mô hình LLM như ChatGPT và Claude trong việc truy xuất tài nguyên lập trình.

Bản dịch AI

Dogfooding at scale: migrating cdnjs to Cloudflare’s Developer Platform

Tính đến ngày 23 tháng 6 năm 2026, cdnjs, một trong những CDN mã nguồn mở bận rộn nhất Internet, đang chạy hoàn toàn trên Nền tảng Nhà phát triển (Developer Platform) của Cloudflare. Trong quá trình này, cdnjs đã bộc lộ những giới hạn của nền tảng, và nền tảng cũng đã phát triển để đáp ứng những giới hạn đó.

cdnjs là một mạng lưới phân phối nội dung (CDN) miễn phí, mã nguồn mở dành cho các thư viện JavaScript và CSS. Thay vì sử dụng trình đóng gói (bundler) hoặc tự lưu trữ jQuery, Bootstrap hay Lodash, bạn chỉ cần chèn một thẻ <script> trỏ đến cdnjs.cloudflare.com và thư viện sẽ được tải từ edge của Cloudflare, ngay lập tức, ở bất kỳ đâu trên thế giới, mà không cần đăng ký, không cần khóa API và không bị giới hạn tốc độ (rate limit). Đây chính là cơ sở hạ tầng đứng sau một phần đáng kể các bài hướng dẫn "nhập môn JavaScript", các bản demo trên CodePen và các câu trả lời trên Stack Overflow.

Được vận hành bởi cộng đồng, cdnjs được sử dụng trên khoảng 12% tổng số trang web, chiếm 48,3% thị phần CDN JavaScript. Nó phục vụ trung bình 108.000 yêu cầu mỗi giây, tương đương 9 tỷ yêu cầu mỗi ngày, trên hơn 330 trung tâm dữ liệu của Cloudflare, với tỷ lệ cache hit đạt 98,6%. Thật tuyệt vời, Internet!

Vào năm 2011, khi các trình đóng gói còn là thứ xa lạ, npm mới chỉ ra đời được một năm, và "chỉ cần chèn một thẻ <script>" là cách web phân phối JavaScript, Ryan Kirkman và Thomas Davis đã xây dựng cdnjs như một bản sao miễn phí, do cộng đồng quản lý của mọi thư viện mã nguồn mở phổ biến.

Cloudflare đã tham gia lưu trữ miễn phí vài tháng sau đó và tiếp quản việc bảo trì dự án vào năm 2019. Khi đó, Cloudflare chưa có một Nền tảng Nhà phát triển hoàn thiện để có thể duy trì toàn bộ hệ sinh thái cdnjs. Mười lăm năm và rất nhiều khối xây dựng sau đó, nền tảng này đã đủ trưởng thành để vận hành cdnjs từ đầu đến cuối, trên Workers, Workflows, D1, Queues, Workers Cache, R2, KV và Containers.

Tại sao cdnjs có 9 tỷ yêu cầu mỗi ngày

Web đã thay đổi đến mức không thể nhận ra so với những ngày đó. Chúng ta có ES Modules (ESM), cú pháp import/export tiêu chuẩn mà các trình duyệt hiểu được một cách tự nhiên. Chúng ta có import maps, Vite, Bun, Turbopack. Chúng ta có các trợ lý AI có thể tạo khung cho toàn bộ ứng dụng trong vài giây. Các trình đóng gói xuất hiện ở khắp mọi nơi. Vậy tại sao một CDN dành cho các thẻ <script> vẫn phục vụ 9 tỷ yêu cầu mỗi ngày?

Một lý do: Các LLM rất ưa chuộng cdnjs. Khi ChatGPT, Claude hoặc Cursor tạo khung cho một bản demo HTML nhanh, chúng thường tìm đến cdnjs vì dữ liệu huấn luyện của chúng chứa đầy các liên kết này. Đã có 15 năm các bài đăng trên blog, tệp README trên GitHub, các trang hướng dẫn và các luồng hỏi đáp trỏ đến cdnjs.cloudflare.com. Cấu trúc URL nhất quán và các phiên bản là bất biến — chính xác là loại phụ thuộc mà một mô hình có thể tạo ra một cách đáng tin cậy mà không bị "ảo giác".

Mọi tệp trên cdnjs đều có mã băm SRI (chúng tôi vẫn đang nỗ lực đảm bảo tất cả các mã băm được lưu trữ hiện tại khớp với thực tế do các lỗi trong hệ thống cũ), các bản sao có thể kiểm chứng và toàn bộ dự án là mã nguồn mở. Trong một thế giới ngày càng lo ngại về các cuộc tấn công chuỗi cung ứng, một bản sao bất biến, được xác minh bằng mã băm của các thư viện nổi tiếng là điều không thể thiếu.

Và nó miễn phí, mãi mãi, cho tất cả mọi người. Không khóa API. Không giới hạn tốc độ. Không "đăng ký để tiếp tục". Đó là một điều hiếm có trên Internet ngày nay, và nó rất đáng để bảo vệ.

Tại sao chúng tôi di chuyển

Chúng tôi không di chuyển vì cdnjs chậm. Chúng tôi di chuyển vì muốn tiếp tục cải thiện nó.

Kiến trúc trước đây đã phục vụ người dùng tốt: 98% cache hit, hàng tỷ yêu cầu, không có sự cố. Nhưng về mặt nội bộ, việc triển khai bất cứ thứ gì mới hoặc sửa các vấn đề hiện có trong cách xử lý gói đang trở nên khó khăn hơn. Việc thực hiện thay đổi đồng nghĩa với việc phải phối hợp triển khai trên GCP Functions, một máy ảo (VM) và Cloudflare. Khả năng quan sát (observability) cũng rất đau đầu.

Những điểm đau đầu

Năm 2020, chúng tôi đã chuyển cdnjs sang serverless, đưa việc phân phối tệp lên Cloudflare Workers và KV, với một máy chủ gốc (origin) bare-metal làm phương án dự phòng. Thay đổi đó đã cải thiện đáng kể khả năng phục hồi và khả năng mở rộng, đồng thời cho phép chúng tôi nén trước mọi tài sản bằng Brotli và gzip để có phản hồi nhỏ hơn, nhanh hơn, nhưng chỉ ở phía phân phối.

Phía xuất bản — đường ống (pipeline) theo dõi npm và GitHub để tìm các phiên bản thư viện mới, tải xuống, xử lý và ghi kết quả để cdnjs có thể phân phối — vẫn nằm trên Google Cloud Platform (GCP). Vào thời điểm đó, Cloudflare Workers được thiết kế cho các yêu cầu HTTP nhanh, tồn tại trong thời gian ngắn; chúng chưa có các khối xây dựng cho một đường ống chạy dài, nhiều bước để tìm nạp các tệp tarball lớn, chạy nén nặng CPU và điều phối công việc trong nhiều giờ. Workflows, Queues, Durable Objects, R2 và Containers khi đó chưa tồn tại.

Vì vậy, chúng tôi đã xây dựng bot xuất bản trên những gì có sẵn: một chuỗi các GCP Functions, một máy ảo chạy git-sync và một kho lưu trữ GitHub làm nguồn sự thật (source of truth). Nó hoạt động, nhưng sáu năm sau, kiến trúc đó đã cho thấy sự lỗi thời. Đây là sơ đồ của kiến trúc trước đây:

Kiến trúc này có năm điểm đau đầu. Điều gây nhức nhối nhất là khả năng quan sát: gỡ lỗi đồng nghĩa với việc phải chắp vá các tệp nhật ký (log) lại với nhau bằng tay. Chúng ta sẽ bắt đầu từ đó.

Vấn đề không phải là thất bại hoàn toàn, mà là thành công một phần. Một phiên bản được xử lý sạch sẽ, ghi vào KV, sau đó âm thầm thất bại khi đưa vào kho lưu trữ GitHub vẫn sẽ phục vụ tốt trong nhiều tuần cho đến khi ai đó nhận ra hai kho lưu trữ đã lệch nhau. Không có cảnh báo nào cho việc đó. Không thể có, vì không có gì trong hệ thống biết được trạng thái đầy đủ của đường ống.

Xin gửi lời cảm ơn chân thành đến đội ngũ GitHub vì đã lưu trữ "gã khổng lồ" này trong hơn một thập kỷ. Họ đã kiên nhẫn cùng chúng tôi qua nhiều năm tăng trưởng dung lượng lưu trữ, và dự án sẽ không thể tồn tại nếu thiếu họ.

Một lợi ích thầm lặng đi kèm với việc di chuyển là có ít thành phần cần bảo mật hơn. Cloud Functions, máy ảo git-sync, hình ảnh container, các bucket GCS, khóa tài khoản dịch vụ — mỗi thứ đó đều là một đối tượng cần bảo mật, vá lỗi và kiểm toán. Việc loại bỏ đường ống này đã đóng lại tất cả các lỗ hổng cdnjs mới được phát hiện gần đây.

Cách chúng tôi xây dựng lại

Kiến trúc cdnjs mới chạy hoàn toàn trên Nền tảng Nhà phát triển của Cloudflare.

R2 là nguồn sự thật duy nhất cho nội dung tệp. Nó không có giới hạn kích thước thực tế, vì vậy các tệp trước đây không thể chứa trong KV, như source maps, các gói lớn và bộ phông chữ, giờ đây nằm cùng với mọi thứ khác. Điểm cộng là API S3 giúp toàn bộ danh mục cdnjs có thể truy cập được bởi bất kỳ ứng dụng khách S3 nào. Bạn muốn duy trì một bản sao? Hãy mở một issue trên kho lưu trữ cdnjs và chúng tôi sẽ cung cấp cho bạn thông tin đăng nhập chỉ đọc.

KV giờ đây chỉ lưu trữ siêu dữ liệu: thông tin gói, danh sách phiên bản, mã băm SRI. KV được xây dựng cho khối lượng đọc cao với tần suất ghi thấp, đó chính xác là hình thái truy cập siêu dữ liệu.

Phía trước Worker là Workers Cache, một bộ nhớ đệm phân tầng mà Cloudflare đã ra mắt trong năm nay. Trước đây, chúng tôi dựa vào một lớp bộ nhớ đệm nội bộ riêng biệt giữa edge và Worker. Lớp đó giờ đã biến mất, được thay thế bằng một lớp thuộc sở hữu của Nền tảng Nhà phát triển, cùng nền tảng vận hành phần còn lại của cdnjs. Bớt đi một thành phần phức tạp!

Kiến trúc mới cũng mở rộng mối quan hệ đối tác lâu dài. DigitalOcean đã lưu trữ trang web cdnjs trong nhiều năm với tư cách là nhà tài trợ; giờ đây họ cũng lưu trữ cả phần lưu trữ. Mọi tệp được xuất bản lên R2 đều được sao chép sang DigitalOcean Spaces: về kiến trúc là bản sao phục hồi sau thảm họa, về vận hành cũng là phương án dự phòng trực tiếp. Worker phân phối sẽ đọc từ đó bất cứ khi nào R2 không thể trả về tệp. Chuỗi là cache → R2 → DigitalOcean, vì vậy nếu R2 gặp sự cố thì cdnjs vẫn không bị sập. Một máy chủ gốc do Cloudflare lưu trữ vẫn nằm trong chuỗi trong quá trình chuyển đổi, nhưng nó sẽ bị loại bỏ khi quá trình sao lưu GitHub hoàn tất vào R2.

Đường ống tiếp nhận (ingestion pipeline) được xây dựng trên Cloudflare Workflows. Cứ mười phút một lần, một cron job sẽ kích hoạt PackageUpdatesWorkflow, kiểm tra npm và GitHub để tìm các phiên bản mới. Với mỗi phiên bản mới được tìm thấy, nó sẽ tạo ra một DownloadPackageWorkflow để tìm nạp tệp tarball vào R2, sau đó là ProcessingWorkflow cho mỗi tệp để giải nén, rút gọn (minify) và nén. Cuối cùng, PublishingWorkflow ghi kết quả vào R2 và KV, đồng thời cập nhật chỉ mục tìm kiếm Algolia.

Vì Workflows cung cấp khả năng thực thi bền bỉ (durable execution), trạng thái của từng bước được bảo toàn. Nếu có bất kỳ lỗi nào xảy ra — hết thời gian chờ mạng, lỗi nén — quy trình làm việc sẽ tiếp tục từ bước thành công cuối cùng.

Phần khó khăn hơn là cách chúng tôi kết nối Workflows với container nén bên ngoài. Chúng tôi nén trước các tệp dựa trên văn bản để hợp lý hóa quy trình phân phối. Nhưng việc nén quá tốn CPU đối với một Worker, vì vậy chúng tôi chuyển nó cho Cloudflare Containers, đợi quá trình nén hoàn tất, rồi tiếp tục công việc từ nơi đã dừng lại.

Đường ống có hai loại chờ đợi:

Theo tệp: Mỗi ProcessingWorkflow ghi tệp chưa nén vào một bucket R2, gửi một công việc đến Queue và chuyển sang trạng thái ngủ (hibernate). Một dịch vụ nén bằng Rust chạy trong container sẽ nhận công việc đó, nén tệp và ghi kết quả vào một bucket khác. Một thông báo sự kiện R2 sẽ đánh thức quy trình làm việc để nó có thể tiếp tục.

Theo gói: Quy trình làm việc cha cần đợi tất cả các tệp con của nó trước khi chuyển sang xuất bản. Một gói có hàng nghìn tệp đồng nghĩa với hàng nghìn tệp con chạy song song. Chúng tôi sử dụng một Durable Object nhỏ làm bộ đếm: cha tăng lên mỗi khi tạo ra một con, con giảm đi khi chúng hoàn thành. Cha sẽ thức dậy khi bộ đếm về bằng không.

Tổng quan về kiến trúc mới, với R2 là nguồn sự thật và Workflows vận hành đường ống:

Vượt qua các giới hạn

Thiết kế kiến trúc mới là một thách thức. Di chuyển danh mục hiện có vào đó mà không làm ảnh hưởng đến một tệp nào đang hoạt động ngoài thực tế lại là một thách thức khác.

Chúng tôi thực sự đã thử điều này một lần trước đây và phải quay lại trạng thái cũ. Kế hoạch khi đó là xử lý lại các gói cũ và ghi kết quả trực tiếp vào R2, nhưng các tệp được tạo lại không khớp byte với những gì KV đã phục vụ. Các trình rút gọn và nén không hoàn toàn xác định (deterministic) trên các phiên bản khác nhau, vì vậy các đầu ra mới tuy đúng nhưng lại có mã băm SRI khác. Đối với một CDN nơi người dùng ghim các mã băm đó trong HTML của họ, đó là một sự cố gián đoạn dịch vụ. Vì vậy, chúng tôi đã quay lại trạng thái cũ, và lần này, chúng tôi di chuyển nội dung hiện có từ KV sang R2 nguyên trạng thay vì tạo lại.

Quyết định đó đã chuyển vấn đề từ "xử lý lại hàng triệu tệp" thành "sao chép hàng triệu tệp giữa các tài khoản mà không bỏ sót tệp nào". Và đó là lúc chúng tôi gặp phải giới hạn subrequest của Workers, bị giới hạn ở mức 1.000 mỗi lần gọi trên các gói trả phí. Một gói có hàng nghìn tệp sẽ vượt quá giới hạn này ngay lập tức. Việc chạy song song cũng không giúp ích gì, vì mọi Worker đều chạm cùng một ngưỡng. Vì vậy, chúng tôi đã phân đoạn quá trình di chuyển theo tên gói và phân tán công việc qua nhiều lần gọi thông qua Queues, với đảm bảo phân phối ít nhất một lần (at-least-once) để không gói nào có thể âm thầm bị loại khỏi quá trình di chuyển.

Chúng tôi đã chạm hai giới hạn của nền tảng trong quá trình di chuyển: 1.000 subrequest mỗi lần gọi Worker và 1.024 bước mỗi Workflow. Thay vì chỉ tìm cách giải quyết tạm thời, chúng tôi đã yêu cầu các đội ngũ Workers và Workflows nâng giới hạn lên — và họ đã làm vậy. Subrequest hiện có thể lên tới 10 triệu trên các gói trả phí; Workflows hiện mặc định là 10.000 bước, có thể cấu hình lên 25.000.

CloudflareCDNHạ tầngLập trìnhCông nghệ
Đọc bài gốc

Bài viết được AI dịch và tổng hợp tự động từ Cloudflare Blog. Liên kết bài gốc ở phía trên. Dữ liệu đồng bộ qua API công khai được ghi nguồn tại AI HOT (canonical) ↗. AIHOT.vn luôn dẫn nguồn đầy đủ — nếu bạn thấy điểm cần chỉnh sửa, hãy gửi ý kiến tại trang phản hồi.