Cache và CDN là hai công cụ quan trọng để tăng tốc website, nhưng chúng giải quyết những lớp vấn đề khác nhau. Cache giúp tái sử dụng dữ liệu hoặc phản hồi đã tạo. CDN đưa nội dung đến vị trí gần người dùng hơn. Khi thiết kế đúng, hệ thống giảm được thời gian phản hồi, số truy vấn cơ sở dữ liệu và tải trên máy chủ gốc.
Bài viết trình bày một chiến lược cache ASP.NET Core theo từng lớp, từ trình duyệt đến ứng dụng và hạ tầng phân phối nội dung. Mục tiêu không phải cache mọi thứ, mà cache đúng dữ liệu trong thời gian phù hợp và có cách làm mới rõ ràng.
Trình duyệt lưu CSS, JavaScript, font và hình ảnh trên thiết bị người dùng. Khi quay lại trang hoặc chuyển sang trang khác dùng chung tài nguyên, trình duyệt không cần tải lại toàn bộ. Hành vi này được điều khiển chủ yếu bằng HTTP header như Cache-Control, ETag và Last-Modified.
CDN lưu bản sao tài nguyên tại các điểm biên. Người dùng nhận dữ liệu từ vị trí gần hơn thay vì luôn kết nối tới máy chủ gốc. CDN đặc biệt hiệu quả với tệp tĩnh, ảnh, video, font và nội dung công khai có thể dùng chung.
Output cache lưu toàn bộ phản hồi HTTP được tạo bởi ứng dụng. Những yêu cầu phù hợp có thể nhận lại phản hồi đã lưu mà không chạy lại toàn bộ pipeline, truy vấn dữ liệu và render giao diện. Từ .NET 7, ASP.NET Core có output caching với chính sách do ứng dụng kiểm soát.
Data cache lưu kết quả truy vấn hoặc dữ liệu tổng hợp trong bộ nhớ hay Redis. Lớp này phù hợp với danh mục, cấu hình website, menu, quyền hoặc dữ liệu đọc nhiều nhưng thay đổi ít.
Với tài nguyên có tên chứa fingerprint, ví dụ app.a83f21.js, có thể đặt thời gian cache dài vì mỗi lần nội dung thay đổi sẽ tạo URL mới. Với HTML hoặc dữ liệu thường xuyên thay đổi, thời gian nên ngắn hơn hoặc yêu cầu xác thực lại.
Một số chỉ thị quan trọng:
public: cho phép trình duyệt và cache dùng chung lưu phản hồi.private: chỉ cache riêng của người dùng được lưu phản hồi.max-age: thời gian phản hồi còn mới ở trình duyệt.s-maxage: thời gian dành cho cache dùng chung như CDN.no-cache: có thể lưu nhưng phải xác thực lại trước khi dùng.no-store: không lưu phản hồi.Không nên đặt cache công khai cho trang có dữ liệu cá nhân, giỏ hàng hoặc thông tin phụ thuộc tài khoản. Một header sai có thể khiến dữ liệu của người này xuất hiện với người khác.
Output cache phù hợp với trang công khai hoặc API đọc nhiều. Cấu hình tối thiểu gồm đăng ký dịch vụ, thêm middleware và áp chính sách cho endpoint:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddOutputCache(options =>
{ options.AddPolicy("PublicCatalog", policy => policy.Expire(TimeSpan.FromMinutes(5)) .SetVaryByQuery("page", "category"));
}); var app = builder.Build(); app.UseRouting();
app.UseOutputCache(); app.MapGet("/api/products", GetProducts) .CacheOutput("PublicCatalog");VaryByQuery rất quan trọng. Nếu kết quả phụ thuộc trang, danh mục, ngôn ngữ hoặc tenant mà khóa cache không chứa yếu tố đó, người dùng có thể nhận sai dữ liệu. Trong hệ thống đa tenant, mã website phải là một phần của khóa cache hoặc policy.
IMemoryCache nhanh, đơn giản và phù hợp với ứng dụng chạy trên một máy chủ. Tuy nhiên, dữ liệu mất khi tiến trình khởi động lại và mỗi máy trong cụm có bản cache riêng.
Redis phù hợp khi ứng dụng chạy nhiều instance hoặc cần cache tồn tại ngoài tiến trình. Đổi lại, Redis tạo thêm kết nối mạng, chi phí vận hành và yêu cầu chiến lược serialize dữ liệu. Không nên đưa Redis vào chỉ vì nghe có vẻ hiện đại. Hãy chọn dựa trên kiến trúc triển khai và mức nhất quán cần thiết.
Cache-aside là mẫu phổ biến: ứng dụng tìm dữ liệu trong cache trước, nếu không có thì đọc từ nguồn, lưu vào cache rồi trả kết quả.
public async Task GetCategoryAsync( int storeId, int categoryId, CancellationToken cancellationToken)
{ var key = $"store:{storeId}:category:{categoryId}"; return await cache.GetOrCreateAsync(key, async entry => { entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10); return await db.Categories .AsNoTracking() .FirstAsync(x => x.Id == categoryId, cancellationToken); });
} Khóa cache cần mô tả đầy đủ mọi đầu vào làm thay đổi kết quả. Với phân trang, hãy cân nhắc trang hiện tại, kích thước trang, bộ lọc, thứ tự, ngôn ngữ và tenant.
Khó nhất của cache không phải lưu dữ liệu mà là biết khi nào dữ liệu hết đúng. Có ba chiến lược chính:
Trong thực tế, cách kết hợp thường tốt nhất: xóa chủ động sau thao tác ghi và vẫn đặt thời gian hết hạn để hệ thống tự phục hồi nếu một sự kiện xóa bị bỏ lỡ.
CSS, JavaScript, font và ảnh có URL version hóa là ứng viên tốt nhất. HTML công khai cũng có thể cache ở CDN nếu không chứa dữ liệu theo người dùng và có cơ chế purge khi xuất bản nội dung mới.
CDN không chỉ giảm khoảng cách địa lý. Nó còn hấp thụ lượng truy cập lớn, giảm số kết nối đến origin và có thể thực hiện nén, chuyển đổi ảnh hoặc bảo vệ lớp biên. Tuy nhiên, cần kiểm tra cache key, query string, cookie và header Vary để tránh tạo quá nhiều biến thể hoặc trả nhầm nội dung.
Khi một khóa phổ biến hết hạn, hàng trăm request có thể cùng truy vấn cơ sở dữ liệu để tạo lại dữ liệu. Hiện tượng này gọi là cache stampede. Có thể giảm rủi ro bằng khóa đồng bộ theo cache key, làm mới sớm có độ ngẫu nhiên hoặc phục vụ dữ liệu cũ trong lúc một request cập nhật bản mới.
Với output caching, cơ chế resource locking có thể giúp chỉ một request tạo phản hồi mới cho cùng khóa. Cần đo tải thật trước khi tắt cơ chế này.
Một chiến lược cache tốt được thiết kế theo lớp. Trình duyệt và CDN xử lý tài nguyên có thể tái sử dụng, output cache giảm chi phí tạo phản hồi, còn memory cache hoặc Redis giảm truy vấn dữ liệu. Hiệu năng chỉ bền vững khi cache key đầy đủ, chính sách hết hạn rõ ràng và thao tác cập nhật có cơ chế làm mới tương ứng.
Nguồn tham khảo:Output caching middleware in ASP.NET Core, Response caching in ASP.NET Core, Serve static files in ASP.NET Core.
Cập nhật tin tức nhanh nhất và mới nhất