Đây là bài báo của Jon Davis. Để duy trì tính dễ đọc, tôi cắt bỏ phần EntLib hiện đã lỗi thời, phần giới thiệu cũng như phần kết luận.
ASP.NET Cache
ASP.NET, hoặc tập hợp System.Web.dll, có cơ chế lưu vào bộ nhớ đệm. Nó không bao giờ được dự định sử dụng bên ngoài ngữ cảnh web, nhưng nó có thể được sử dụng bên ngoài web và nó thực hiện tất cả các hành vi hết hạn ở trên trong một bảng băm của các loại.
Sau khi tìm kiếm trên Google, có vẻ như khá nhiều người đã thảo luận về chức năng bộ nhớ đệm tích hợp trong .NET đã sử dụng bộ đệm ASP.NET trong các dự án không phải web của họ. Đây không còn là hệ thống bộ nhớ đệm tích hợp sẵn có nhất, được hỗ trợ nhiều nhất trong .NET; .NET 4 có ObjectCache mà tôi sẽ trình bày sau. Microsoft luôn kiên quyết rằng bộ đệm ASP.NET không được dùng để sử dụng bên ngoài web. Nhưng nhiều người vẫn bị mắc kẹt trong .NET 2.0 và .NET 3.5 và cần một thứ gì đó để làm việc và điều này xảy ra với nhiều người, mặc dù MSDN nói rõ ràng:
Lưu ý: Lớp Cache không nhằm mục đích sử dụng bên ngoài các ứng dụng ASP.NET. Nó được thiết kế và thử nghiệm để sử dụng trong ASP.NET nhằm cung cấp bộ nhớ đệm cho các ứng dụng Web. Trong các loại ứng dụng khác, chẳng hạn như ứng dụng bảng điều khiển hoặc ứng dụng Windows Forms, bộ nhớ đệm ASP.NET có thể không hoạt động chính xác.
Lớp cho bộ đệm ASP.NET là System.Web.Caching.Cache trong System.Web.dll. Tuy nhiên, bạn không thể chỉ tạo mới một đối tượng Cache. Bạn phải lấy nó từ System.Web.HttpRuntime.Cache.
Cache cache = System.Web.HttpRuntime.Cache;
Làm việc với bộ đệm ASP.NET được ghi lại trên MSDN tại đây .
Ưu điểm:
- Nó được tích hợp sẵn .
- Mặc dù có cú pháp .NET 1.0, nó khá đơn giản để sử dụng.
- Khi được sử dụng trong ngữ cảnh web, nó đã được thử nghiệm tốt . Bên ngoài ngữ cảnh web, theo các tìm kiếm của Google, nó không thường được biết là nguyên nhân gây ra sự cố, mặc dù Microsoft đã khuyến cáo chống lại nó, miễn là bạn đang sử dụng .NET 2.0 trở lên.
- Bạn có thể được thông báo qua người được ủy quyền khi một mục bị xóa, điều này là cần thiết nếu bạn cần giữ nó tồn tại và bạn không thể đặt trước mức độ ưu tiên của mục đó.
- Các mục riêng lẻ có tính linh hoạt của bất kỳ phương pháp (a), (b) hoặc (c) nào về việc hết hạn và xóa trong danh sách các phương pháp xóa ở đầu bài viết này. Bạn cũng có thể liên kết hành vi hết hạn với sự hiện diện của một tệp vật lý.
Nhược điểm:
- Không chỉ là nó tĩnh, chỉ có một . Bạn không thể tạo kiểu của riêng mình với phiên bản Cache tĩnh của riêng nó. Bạn chỉ có thể có một nhóm cho toàn bộ ứng dụng của mình, khoảng thời gian. Bạn có thể bọc thùng bằng các trình bao bọc của riêng mình để thực hiện những việc như thêm tiền tố vào các khóa và xóa các tiền tố này khi bạn kéo các cặp khóa / giá trị ra ngoài. Nhưng vẫn chỉ có một cái xô. Mọi thứ được gộp lại với nhau. Điều này có thể gây phiền toái thực sự nếu chẳng hạn như bạn có một dịch vụ cần lưu vào bộ đệm ba hoặc bốn loại dữ liệu khác nhau một cách riêng biệt. Đây không phải là một vấn đề lớn đối với các dự án đơn giản đến mức thảm hại. Nhưng nếu một dự án có bất kỳ mức độ phức tạp đáng kể nào do yêu cầu của nó, bộ đệm ASP.NET thường sẽ không đủ.
- Các mục có thể biến mất, willy-nilly. Rất nhiều người không biết về điều này — tôi đã không làm như vậy, cho đến khi tôi cập nhật kiến thức của mình về việc triển khai bộ nhớ cache này. Theo mặc định, bộ đệm ASP.NET được thiết kế để hủy các mục khi nó “cảm thấy” thích nó. Cụ thể hơn, hãy xem (c) trong định nghĩa của tôi về bảng bộ nhớ cache ở đầu bài viết này. Nếu một luồng khác trong cùng một quy trình đang hoạt động trên một thứ hoàn toàn khác và nó đưa các mục có mức độ ưu tiên cao vào bộ nhớ đệm, thì ngay sau khi .NET quyết định nó cần yêu cầu một số bộ nhớ, nó sẽ bắt đầu hủy một số mục trong bộ đệm theo ưu tiên của họ, ưu tiên thấp hơn trước. Tất cả các ví dụ được ghi lại ở đây để thêm các mục trong bộ nhớ cache đều sử dụng mức ưu tiên mặc định, thay vì giá trị ưu tiên NotRemovable, giá trị này không bị xóa vì mục đích xóa bộ nhớ nhưng vẫn sẽ xóa nó theo chính sách hết hạn.
- Khóa phải là một chuỗi. Ví dụ: nếu bạn đang lưu vào bộ nhớ đệm các bản ghi dữ liệu trong đó các bản ghi được khóa trên một số dài hoặc một số nguyên, trước tiên bạn phải chuyển đổi khóa thành một chuỗi.
- Cú pháp cũ . Đó là cú pháp .NET 1.0, thậm chí còn xấu hơn ArrayList hoặc Hashtable. Không có generic ở đây, không có giao diện IDictionary <>. Nó không có phương thức Contains (), không có bộ sưu tập Keys, không có sự kiện tiêu chuẩn; nó chỉ có một phương thức Get () cộng với một trình lập chỉ mục thực hiện tương tự như Get (), trả về null nếu không có kết quả phù hợp, cộng với Add (), Insert () (thừa?), Remove () và GetEnumerator () .
- Bỏ qua nguyên tắc KHÔ về việc thiết lập các hành vi hết hạn / xóa mặc định của bạn để bạn có thể quên chúng. Bạn phải thông báo rõ ràng với bộ nhớ cache về cách bạn muốn mục bạn đang thêm hết hạn hoặc bị xóa mỗi khi bạn thêm mục.
- Không có cách nào để truy cập chi tiết bộ nhớ đệm của một mục được lưu trong bộ nhớ cache, chẳng hạn như dấu thời gian của thời điểm nó được thêm vào. Tính năng đóng gói đã diễn ra hơi quá đà ở đây, khiến việc sử dụng bộ đệm ẩn gặp khó khăn khi bạn đang cố gắng xác định xem một mục được lưu trong bộ đệm có bị vô hiệu so với cơ chế bộ đệm khác (tức là bộ sưu tập phiên) hay không.
- Sự kiện xóa không được hiển thị dưới dạng sự kiện và phải được theo dõi tại thời điểm thêm.
- Và nếu tôi chưa nói đủ, Microsoft khuyến cáo rõ ràng không nên sử dụng nó bên ngoài web. Và nếu bạn bị nguyền rủa với .NET 1.1, bạn không nên sử dụng nó với bất kỳ sự tin cậy nào về sự ổn định bên ngoài web, vì vậy đừng bận tâm.
ObjectCache / MemoryCache của .NET 4.0
Microsoft cuối cùng đã triển khai một lớp ObjectCache trừu tượng trong phiên bản mới nhất của .NET Framework và triển khai MemoryCache kế thừa và triển khai ObjectCache cho các mục đích trong bộ nhớ trong cài đặt không phải web.
System.Runtime.Caching.ObjectCache nằm trong cụm System.Runtime.Caching.dll. Nó là một lớp trừu tượng khai báo về cơ bản giống với các giao diện kiểu .NET 1.0 được tìm thấy trong bộ đệm ASP.NET. System.Runtime.Caching.MemoryCache
là triển khai trong bộ nhớ của ObjectCache và rất giống với ASP.NET cache, với một vài thay đổi.
Để thêm một mặt hàng có thời hạn trượt, mã của bạn sẽ trông giống như sau:
var config = new NameValueCollection();
var cache = new MemoryCache("myMemCache", config);
cache.Add(new CacheItem("a", "b"),
new CacheItemPolicy
{
Priority = CacheItemPriority.NotRemovable,
SlidingExpiration=TimeSpan.FromMinutes(30)
});
Ưu điểm:
- Nó được tích hợp sẵn và hiện được Microsoft hỗ trợ và khuyến nghị bên ngoài web.
Không giống như ASP.NET cache, bạn có thể khởi tạo một thể hiện đối tượng MemoryCache.
Lưu ý: Nó không nhất thiết phải tĩnh, nhưng nó phải là - đó là khuyến nghị của Microsoft (xem phần Thận trọng màu vàng) .
Một số cải tiến nhỏ đã được thực hiện so với giao diện của bộ đệm ASP.NET, chẳng hạn như khả năng đăng ký các sự kiện xóa mà không nhất thiết phải ở đó khi các mục được thêm vào, Chèn thừa () bị xóa, các mục có thể được thêm bằng CacheItem đối tượng có bộ khởi tạo xác định chiến lược bộ nhớ đệm và Chứa () đã được thêm vào.
Nhược điểm:
- Vẫn không hoàn toàn củng cố DRY. Từ kinh nghiệm ít ỏi của tôi, bạn vẫn không thể đặt TimeSpan hết hạn trượt một lần và quên nó đi. Và thẳng thắn mà nói, mặc dù chính sách trong mẫu thêm mục ở trên dễ đọc hơn, nhưng nó đòi hỏi sự chi tiết kinh khủng.
- Nó vẫn không được khóa chung chung; nó yêu cầu một chuỗi làm khóa. Vì vậy, bạn không thể lưu trữ dài hoặc int nếu bạn đang lưu bản ghi dữ liệu vào bộ nhớ đệm, trừ khi bạn chuyển đổi thành chuỗi.
Tự làm: Tự xây một
Thực ra khá đơn giản để tạo một từ điển bộ nhớ đệm thực hiện hết hạn rõ ràng hoặc trượt. (Sẽ khó hơn rất nhiều nếu bạn muốn các mục được tự động xóa vì mục đích xóa bộ nhớ.) Đây là tất cả những gì bạn phải làm:
- Tạo một lớp chứa giá trị có tên là Expiring hoặc Expirable sẽ chứa giá trị kiểu T, thuộc tính TimeStamp thuộc loại DateTime để lưu trữ khi giá trị được thêm vào bộ nhớ cache và TimeSpan sẽ cho biết khoảng cách so với dấu thời gian mà mặt hàng sẽ hết hạn. Đối với ngày hết hạn rõ ràng, bạn có thể chỉ cần hiển thị một bộ thiết lập thuộc tính đặt TimeSpan cho một ngày bị trừ đi bởi dấu thời gian.
- Tạo một lớp, chúng ta hãy gọi nó là ExpirableItemsDictionary, thực hiện IDictionary. Tôi muốn làm cho nó trở thành một lớp chung chung được xác định bởi người tiêu dùng.
- Trong lớp được tạo ở # 2, thêm Từ điển> làm thuộc tính và gọi nó là InnerDictionary.
- Việc triển khai nếu IDictionary trong lớp được tạo ở # 2 sẽ sử dụng InnerDictionary để lưu trữ các mục được lưu trong bộ nhớ cache. Tính năng đóng gói sẽ ẩn các chi tiết của phương thức lưu vào bộ nhớ đệm thông qua các trường hợp của kiểu được tạo trong # 1 ở trên.
- Đảm bảo rằng trình lập chỉ mục (this []), ContainsKey (), v.v., cẩn thận loại bỏ các mục đã hết hạn và loại bỏ các mục đã hết hạn trước khi trả về giá trị. Trả lại null trong getters nếu mục đã bị xóa.
- Sử dụng khóa luồng trên tất cả getters, setters, ContainsKey () và đặc biệt khi xóa các mục đã hết hạn.
- Tăng sự kiện bất cứ khi nào một mục bị xóa do hết hạn.
- Thêm một phiên bản System.Threading.Timer và cố định nó trong quá trình khởi tạo để tự động xóa các mục đã hết hạn sau mỗi 15 giây. Đây là hành vi tương tự như bộ đệm ASP.NET.
- Bạn có thể muốn thêm một quy trình AddOrUpdate () để đẩy hết hạn trượt bằng cách thay thế dấu thời gian trên vùng chứa của mục (Phiên bản hết hạn) nếu nó đã tồn tại.
Microsoft phải hỗ trợ các thiết kế ban đầu của mình vì cơ sở người dùng của họ đã xây dựng sự phụ thuộc vào chúng, nhưng điều đó không có nghĩa là chúng là những thiết kế tốt.
Ưu điểm:
- Bạn có toàn quyền kiểm soát việc triển khai.
- Có thể củng cố DRY bằng cách thiết lập các hành vi bộ nhớ đệm mặc định và sau đó chỉ cần thả các cặp khóa / giá trị vào mà không cần khai báo chi tiết bộ nhớ đệm mỗi khi bạn thêm một mục.
- Có thể triển khai các giao diện hiện đại , cụ thể là
IDictionary<K,T>
. Điều này làm cho nó dễ sử dụng hơn nhiều vì giao diện của nó dễ dự đoán hơn như một giao diện từ điển, ngoài ra nó còn giúp người trợ giúp và các phương thức mở rộng hoạt động với IDictionary <> dễ tiếp cận hơn.
- Thông tin chi tiết về bộ nhớ đệm có thể không được đóng gói , chẳng hạn như bằng cách hiển thị InnerDictionary của bạn thông qua thuộc tính chỉ đọc công khai, cho phép bạn viết các bài kiểm tra đơn vị rõ ràng chống lại chiến lược bộ nhớ đệm của bạn cũng như mở rộng triển khai bộ nhớ đệm cơ bản của bạn với các chiến lược bộ nhớ đệm bổ sung được xây dựng dựa trên nó.
- Mặc dù nó không nhất thiết phải là một giao diện quen thuộc đối với những người đã tự thoải mái với cú pháp kiểu .NET 1.0 của bộ đệm ASP.NET hoặc Khối ứng dụng bộ đệm, bạn có thể xác định giao diện trông như thế nào theo ý mình muốn.
- Có thể sử dụng bất kỳ loại nào cho chìa khóa. Đây là một lý do tại sao generic được tạo ra. Không phải tất cả mọi thứ nên được khóa bằng một chuỗi.
Nhược điểm:
- Không được phát minh bởi Microsoft, cũng không được xác nhận bởi Microsoft , vì vậy nó sẽ không có được sự đảm bảo chất lượng như nhau.
- Giả sử chỉ thực hiện các hướng dẫn mà tôi mô tả ở trên, không xóa các mục “hoàn toàn hợp lý” để xóa bộ nhớ trên cơ sở ưu tiên (dù sao đây cũng là một chức năng tiện ích ở góc của bộ nhớ cache .. MUA RAM ở nơi bạn sẽ sử dụng bộ nhớ đệm , RAM rẻ).
Trong số tất cả bốn tùy chọn này, đây là tùy chọn của tôi. Tôi đã thực hiện giải pháp bộ nhớ đệm cơ bản này. Cho đến nay, nó dường như hoạt động hoàn hảo, không có lỗi nào được biết đến (vui lòng liên hệ với tôi với các nhận xét bên dưới hoặc tại jon-at-jondavis nếu có !!), và tôi dự định sử dụng nó trong tất cả các dự án phụ nhỏ hơn của tôi cần bộ nhớ đệm cơ bản. Nó đây:
Liên kết Github: https://github.com/kroimon/ExpirableItemDictionary
Liên kết cũ: ExpirableItemDictionary.zip
Đáng được đề cập: AppFainst, NoSQL, Et Al
Lưu ý rằng tiêu đề của bài viết blog này cho biết “Bộ nhớ đệm đơn giản”, không phải “Bộ nhớ đệm hạng nặng”. Nếu bạn muốn tham gia vào những thứ hạng nặng, bạn nên xem xét các giải pháp chuyên dụng, quy mô.