.Net快取管理框架CacheManager
Cache 快取在計算機領域是一個被普遍使用的概念。硬體中CPU有一級快取,二級快取, 瀏覽器中有快取,軟體開發中也有分散式快取memcache, redis。快取無處不在的原因是它能夠極大地提高硬體和軟體的執行速度。在專案開發中,效能慢的地方常常是IO操作頻繁的地方,讀取資料庫是我們常見的消耗效能的地方。這個時候,如果將使用頻繁的資料快取到能夠高速讀取的介質中,下次訪問時,不用再去請求資料庫,直接從快取中獲取所需的資料,就能夠大大提高效能。這篇文章主要討論的是在.Net開發中,如何使用CacheManager框架方便的管理專案中的快取。
一,CacheManager介紹以及優點
CacheManager是開源的.Net快取管理框架。它不是具體的快取實現,而是在快取之上,方便開發人員配置和管理各種不同的快取,為上層應用程式提供統一的快取介面的中間層。
下面是CacheManager的一些優點:
- 讓開發人員的生活更容易處理和配資快取,即使是非常複雜的快取方案。
- CacheManager能夠管理多種快取,包含 記憶體, appfabric, redis, couchbase, windows azure cache, memorycache等。
- 提供了額外的功能,如快取同步、併發更新、事件、效能計數器等…
二,CacheManager開始之旅
CacheManager上手還是非常簡單的。下面使用記憶體快取結合CacheManager的一個例項,能夠幫助我們快速的熟悉CacheManager如何使用。
首先在Visual Studio中建立一個Console Application.
使用 Nuget 為專案新增CacheManager包引用。CacheManager包含了很多的Package. 其中CacheManager.Core是必須的,其它的針對不同快取平臺上有不同的對應Package.
這個Demo中,我們使用記憶體作為快取,所以只是需要CacheManager.Core和CacheManager.SystemRuntimeCaching
接著在Main函式中配置好我們的快取:
1 using System; 2 using CacheManager.Core; 3 namespace ConsoleApplication 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 var cache = CacheFactory.Build("getStartedCache", settings => 10 { 11 settings.WithSystemRuntimeCacheHandle("handleName"); 12 }); 13 } 14 } 15 }
上面程式碼中使用CacheFactory建立了一個名稱為getStartedCache的快取例項,這個快取例項使用的是SystemRunTime Cache, 記憶體快取。一個快取例項是可以配置多個Handle的,我們可以使用記憶體來作為儲存介質,也可以使用Redis分散式快取作為儲存介質,並且可以同時在一個快取例項中使用,後面我們再介紹多級快取的配置和使用。
接下來,我們新增一些測試快取的程式碼
1 static void Main(string[] args) 2 { 3 4 var cache = CacheFactory.Build("getStartedCache", settings => 5 { 6 settings.WithSystemRuntimeCacheHandle("handleName"); 7 }); 8 9 cache.Add("keyA", "valueA"); 10 cache.Put("keyB", 23); 11 cache.Update("keyB", v => 42); 12 Console.WriteLine("KeyA is " + cache.Get("keyA")); // should be valueA 13 Console.WriteLine("KeyB is " + cache.Get("keyB")); // should be 42 14 cache.Remove("keyA"); 15 Console.WriteLine("KeyA removed? " + (cache.Get("keyA") == null).ToString()); 16 Console.WriteLine("We are done..."); 17 Console.ReadKey(); 18 }
三,CacheManager多級快取配置
實際開發中,我們常常會需要使用多級快取。
一種常見的情況是,你有一個分散式式快取伺服器,例如redis,獨立的快取伺服器能夠讓我們的多個系統應用程式都能夠共享這些快取的資料,因為這些快取項的建立是昂貴的。
和訪問資料庫相比,分散式快取速度較快,但是和記憶體相比,還是不夠快。因為分散式快取使用還需要序列化和網路傳輸的時間消耗。
這個時候裡,做個分級快取是個好的解決方案,將記憶體快取結合分散式快取使用,使用頻率高的資料直接從記憶體中讀取,這將大大提高應用程式的整體效能。
使用記憶體快取的讀取速度能夠達到分散式快取的100倍,甚至更高。
使用CacheManager, 配置多級快取是一件非常容易的事情
1 var cache = CacheFactory.Build<int>("myCache", settings => 2 { 3 settings 4 .WithSystemRuntimeCacheHandle("inProcessCache")//記憶體快取Handle 5 .And 6 .WithRedisConfiguration("redis", config =>//Redis快取配置 7 { 8 config.WithAllowAdmin() 9 .WithDatabase(0) 10 .WithEndpoint("localhost", 6379); 11 }) 12 .WithMaxRetries(1000)//嘗試次數 13 .WithRetryTimeout(100)//嘗試超時時間 14 .WithRedisBackPlate("redis")//redis使用Back Plate 15 .WithRedisCacheHandle("redis", true);//redis快取handle 16 });
上面程式碼中,記憶體快取和Redis快取配置部分很容易看明白。但是BackPlate是什麼作用? 接下來,我們看看CacheManager中的BackPlate擋板機制。
四, BackPlate解決分散式快取中的同步問題
對於大型的軟體系統,常常都是分為很多獨立的子專案,各個子專案為了節約成本或者是方便資料共享,常常會共用同一個分佈快取伺服器。這樣在使用多級快取的時候,就有可能出現數據不一致的情況。
假設在系統A中的更新了快取中的一個數據項,這個時候CacheManager會在A設定的所有的快取handle中更新改資料,這裡也包括了分散式快取上的資料。但是在系統B中的記憶體快取中,還是會存在著舊的未更新的資料。當系統B從快取中取這條記錄的時候,就會出現記憶體快取和分散式快取中的資料不一致的情況。
為了防止這一點,快取管理器有一個功能叫做cachebackplate將嘗試同步多個系統中的快取。
上面設定的多級快取中,我們就將redis作為BackPlate的源. 也就是說所有的資料都需要以redis中快取的資料為藍本。
在設定redis作為BackPlate之後,同樣發生上面的資料不一致的情況的時候,只要redis中的資料被修改了,就會觸發CacheManager更新所有系統中的記憶體快取中的資料,和redis中的資料保持一致。
同步的工作是如何完成的?
每次一條快取記錄被刪除或更新的時候,Cache Manager會發送一個訊息,讓BackPlate儲存這次的資料變化資訊。所有其它的系統將非同步接收這些訊息,並將相應地作出更新和刪除操作,保證資料的一致性。
五,ExpirationMode和CacheUpdateMode
涉及到快取,就必然有快取過期的問題。CacheManager中提供了一些簡單的快取過期方式設定。
1 public enum ExpirationMode 2 { 3 None = 0, 4 Sliding = 1, 5 Absolute = 2, 6 }
同時CacheManager還為多級快取之間設定不同的資料更新策略
1 public enum CacheUpdateMode 2 { 3 None = 0, 4 Full = 1, 5 Up = 2, 6 }
使用Sliding和Up, 我們我可以為多級快取設定不同的快取過期時間,這樣使用頻率高的資料就能夠儲存在訪問速度更快的記憶體中,訪問頻率次高的放到分散式快取中。當CacheManager在記憶體中找不到快取資料的時候,就會嘗試在分散式快取中找。找到後,根據Up設定,會再將該快取資料儲存到記憶體快取中。
具體的配置方式如下:
1 var cache = CacheFactory.Build<int>("myCache", settings => 2 { 3 settings.WithUpdateMode(CacheUpdateMode.Up) 4 .WithSystemRuntimeCacheHandle("inProcessCache")//記憶體快取Handle 5 .WithExpiration(ExpirationMode.Sliding, TimeSpan.FromSeconds(60))) 6 .And 7 .WithRedisConfiguration("redis", config =>//Redis快取配置 8 { 9 config.WithAllowAdmin() 10 .WithDatabase(0) 11 .WithEndpoint("localhost", 6379); 12 }). 13 .WithExpiration(ExpirationMode.Sliding, TimeSpan. FromHours (24))) 14 .WithMaxRetries(1000)//嘗試次數 15 .WithRetryTimeout(100)//嘗試超時時間 16 .WithRedisBackPlate("redis")//redis使用Back Plate 17 .WithRedisCacheHandle("redis", true);//redis快取handle 18 19 });
六,快取使用分析
在快取使用中,對於快取hit和miss資料態比較關係,這些資料能夠幫助我們分析和調整快取的設定,幫助快取使用地更加合理。
1 var cache = CacheFactory.Build("cacheName", settings => settings 2 .WithSystemRuntimeCacheHandle("handleName") 3 .EnableStatistics() 4 .EnablePerformanceCounters());
在配置好快取的Statistic功能後,我們就能夠跟蹤到快取的使用情況了, 下面就是分別列印各個快取handle中的分析資料。
1 foreach (var handle in cache.CacheHandles) 2 { 3 var stats = handle.Stats; 4 Console.WriteLine(string.Format( 5 "Items: {0}, Hits: {1}, Miss: {2}, Remove: {3}, ClearRegion: {4}, Clear: {5}, Adds: {6}, Puts: {7}, Gets: {8}", 6 stats.GetStatistic(CacheStatsCounterType.Items), 7 stats.GetStatistic(CacheStatsCounterType.Hits), 8 stats.GetStatistic(CacheStatsCounterType.Misses), 9 stats.GetStatistic(CacheStatsCounterType.RemoveCalls), 10 stats.GetStatistic(CacheStatsCounterType.ClearRegionCalls), 11 stats.GetStatistic(CacheStatsCounterType.ClearCalls), 12 stats.GetStatistic(CacheStatsCounterType.AddCalls), 13 stats.GetStatistic(CacheStatsCounterType.PutCalls), 14 stats.GetStatistic(CacheStatsCounterType.GetCalls) 15 )); 16 }
七,結尾
快取是個好東西,用好了能夠極大的提高效能。快取的使用本身是個很大的話題,這邊文章只是從快取管理這個角度介紹了CachManager的使用。