在我們開發的很多分布式項目裡面(如基於WCF服務、Web API服務方式),由於數據提供涉及到數據庫的相關操作,如果客戶端的並發數量超過一定的數量,那麼數據庫的請求處理則以爆發式增長,如果數據庫服務器無法快速處理這些並發請求,那麼將會增加客戶端的請求時間,嚴重者可能導致數據庫服務或者應用服務直接癱瘓。緩存方案就是為這個而誕生,隨著緩存的引入,可以把數據庫的IO耗時操作,轉換為內存數據的快速響應操作,或者把整個頁面緩存到緩存系統裡面。緩存框架在各個平台裡面都有很多的實現,基本上多數是采用分布式緩存Redis、Memcached來實現。本系列文章介紹在.NET平台中,使用開源緩存框架CacheManager來實現數據的緩存的整個過程,本篇主要介紹CacheManager的使用和相關的測試。
CacheManager是一個以C#語言開發的開源.Net緩存框架抽象層。它不是具體的緩存實現,但它支持多種緩存提供者(如Redis、Memcached等)並提供很多高級特性。
CacheManager 主要的目的使開發者更容易處理各種復雜的緩存場景,使用CacheManager可以實現多層的緩存,讓進程內緩存在分布式緩存之前,且僅需幾行代碼來處理。
CacheManager 不僅僅是一個接口去統一不同緩存提供者的編程模型,它使我們在一個項目裡面改變緩存策略變得非常容易,同時也提供更多的特性:如緩存同步、並發更新、序列號、事件處理、性能計算等等,開發人員可以在需要的時候選擇這些特性。
CacheManager的GitHub源碼地址為:https://github.com/MichaCo/CacheManager,如果需要具體的Demo及說明,可以訪問其官網:http://cachemanager.net/。
使用Nuget為項目添加CacheManager包引用。CacheManager包含了很多的Package. 其中CacheManager.Core是必須的,其它的針對不同緩存平台上有不同的對應Package,整個Nuget包包含下面幾個部分的內容。
CacheManager緩存框架支持Winform和Web等應用開發,以及支持多種流行的緩存實現,如MemoryCache、Redis、Memcached、Couchbase、System.Web.Caching等。
縱觀整個緩存框架,它的特定很明顯,在支持多種緩存實現外,本身主要是以內存緩存(進程內)為主,其他分布式緩存為輔的多層緩存架構方式,以達到快速命中和處理的機制,它們內部有相關的消息處理,使得即使是分布式緩存,也能夠及時實現並發同步的緩存處理。
在網上充斥著基於某種單獨緩存的實現和應用的趨勢下,這種更抽象一層,以及提供更高級特性的緩存框架,在提供了統一編程模型的基礎上,也實現了非常強大的兼容性,使得我一接觸到這個框架,就對它愛不釋手。
在GitHub上,緩存框架的前幾名,除了這個緩存框架外,也還有一些,不過從文檔的豐富程度等各方面來看,這個緩存框架還是非常值得擁有的。
CacheManager緩存框架在配置方面,支持代碼方式的配置、XML配置,以及JSON格式的配置處理,非常方便。
CacheManager緩存框架默認對緩存數據的序列化是采用二進制方式,同時也支持多種自定義序列化的方式,如基於JOSN.NET的JSON序列化或者自定義序列化方式。
CacheManager緩存框架可以對緩存記錄的增加、刪除、更新等相關事件進行記錄。
CacheManager緩存框架的緩存數據是強類型的,可以支持各種常規類型的處理,如Int、String、List類型等各種基礎類型,以及可序列號的各種對象及列表對象。
CacheManager緩存框架支持多層的緩存實現,內部良好的機制可以高效、及時的同步好各層緩存的數據。
CacheManager緩存框架支持對各種操作的日志記錄。
CacheManager緩存框架在分布式緩存實現中支持對更新的鎖定和事務處理,讓緩存保持更好的同步處理,內部機制實現版本沖突處理。
CacheManager緩存框架支持兩種緩存過期的處理,如絕對時間的過期處理,以及固定時段的過期處理,是我們處理緩存過期更加方便。
....
很多特性基本上覆蓋了緩存的常規特性,而且提供的接口基本上也是我們所經常用的Add、Put、Update、Remove等接口,使用起來也非常方便。
通過上面對CacheManager緩存框架的簡單了解,我們大概了解了它應用的一些功能,但是實際上我們如何使用它,我們需要做一些學習和了解,首先我們需要在整個應用框架裡面,知道緩存框架所扮演的角色。
一般來說,對於單機版本的應用場景,基本上是無需引入這種緩存框架的,因為客戶端的並發量很少,而且數據請求也是寥寥可數的,性能方便不會有任何問題。
如果對於分布式的應用系統,如我在很多隨筆中介紹到我的《混合式開發框架》、《Web開發框架》,由於數據請求是並發量隨著用戶增長而增長的,特別對於一些互聯網的應用系統,極端情況下某個時間點一下可能就會達到了整個應用並發的峰值。那麼這種分布式的系統架構,引入數據緩存來降低IO的並發數,把耗時請求轉換為內存的高速請求,可以極大程度的降低系統宕機的風險。
我們以基於常規的Web API層來構建應用框架為例,整個數據緩存層,應該是在Web API層之下、業務實現層之上的一個層,如下所示。
在這個數據緩存層裡面,我們引入了CacheManager緩存框架,實現分布式的緩存處理,使得我們的緩存數據能夠在Redis服務器上實現數據的處理,同時可以在系統重啟的時候,不至於丟失數據,能夠快速恢復緩存數據。
為了實現對這個CacheManager緩存框架的使用,我們需要先進行一個使用測試,以便了解它的各個方便情況,然後才能廣泛應用在我們的數據中間層上。
我們建立一個項目,並在引用的地方打開管理NuGet程序包,然後搜索到CacheManager的相關模塊應用,並加入到項目引用裡面,此為第一步工作。
我們創建一個客戶對象類,用來模擬數據的存儲和顯示的,如下代碼所示。
/// <summary> /// 模擬數據存儲的客戶對象類 /// </summary> public class Customer { private static Customer m_Customer = null; private static ICacheManager<object> manager = null; //初始化列表值 private static List<string> list = new List<string>() { "123", "456", "789" }; /// <summary> /// 客戶對象的單件實例 /// </summary> public static Customer Instance { get { if(m_Customer == null) { m_Customer = new Customer(); } if (manager == null) { manager = CacheFactory.Build("getStartedCache", settings => { settings.WithSystemRuntimeCacheHandle("handleName"); }); } return m_Customer; } }
這個類先做了一個單例的實現,並初始化緩存Customer類對象,以及緩存管理類ICacheManager<object> manager,這個是我們後面用來操作緩存數據的主要引用對象。
我們編寫幾個函數,用來實現對數據的獲取,數據增加、數據刪除的相關操作,並在數據增加、刪除的時候,觸發緩存的更新,這樣我們下次獲取數據的時候,就是最新的數據了。
/// <summary> /// 獲取所有客戶信息 /// </summary> /// <returns></returns> public List<string> GetAll() { var value = manager.Get("GetAll") as List<string>; if(value == null) { value = list;//初始化並加入緩存 manager.Add("GetAll", value); Debug.WriteLine("初始化並加入列表"); } else { Debug.WriteLine("訪問緩存獲取:{0}", DateTime.Now); } return value; } /// <summary> /// 插入新的記錄 /// </summary> /// <param name="customer"></param> /// <returns></returns> public bool Insert(string customer) { //先獲取全部記錄,然後加入記錄 if (!list.Contains(customer)) { list.Add(customer); } //重新設置緩存 manager.Update("GetAll", v => list); return true; } /// <summary> /// 刪除指定記錄 /// </summary> /// <param name="customer"></param> /// <returns></returns> public bool Delete(string customer) { if(list.Contains(customer)) { list.Remove(customer); } manager.Update("GetAll", v=>list); return true; }
我們編寫一個Winform程序來對這個緩存測試,以方便了解其中的機制。
我們在測試讀取的時候,也就是對GetAll進行處理,插入以及刪除主要就是為了測試緩存更新的處理。代碼如下所示。
private void btnTestSimple_Click(object sender, EventArgs e) { var list = Customer.Instance.GetAll(); Debug.WriteLine("客戶端獲取記錄數:{0}", list != null ? list.Count : 0); } private void btnInsert_Click(object sender, EventArgs e) { var name = "abc"; Customer.Instance.Insert(name); Debug.WriteLine(string.Format("插入記錄:{0}", name)); } private void btnDelete_Click(object sender, EventArgs e) { var name = "abc"; Customer.Instance.Delete(name); Debug.WriteLine(string.Format("刪除記錄:{0}", name)); }
我們跟蹤記錄,可以看到下面的日志信息。
我們可以看到,其中第一次是緩存沒有的情況下進行初始化,初始化的記錄數量為3個,然後插入記錄後,再次獲取數據的時候,緩存更新後的數量就變為4個了。
我們前面介紹了插入記錄的後台代碼,它同時進行了緩存數據的更新了。
/// <summary> /// 插入新的記錄 /// </summary> /// <param name="customer"></param> /// <returns></returns> public bool Insert(string customer) { //先獲取全部記錄,然後加入記錄 if (!list.Contains(customer)) { list.Add(customer); } //重新設置緩存 manager.Update("GetAll", v => list); return true; }
我們前面介紹的緩存初始化配置的時候,默認是使用內存緩存的,並沒有使用分布式緩存的配置,它的初始化代碼如下:
manager = CacheFactory.Build("getStartedCache", settings => { settings.WithSystemRuntimeCacheHandle("handleName"); });
我們在正常情況下,還是需要使用這個強大的分布式緩存的,例如我們可以使用Redis的緩存處理,關於Redis的安裝和使用,請參考我的隨筆《基於C#的MongoDB數據庫開發應用(4)--Redis的安裝及使用》。
引入分布式的Redis緩存實現,我們的配置代碼只需要做一定的改變即可,如下所示。
manager = CacheFactory.Build("getStartedCache", settings => { settings.WithSystemRuntimeCacheHandle("handleName") .And .WithRedisConfiguration("redis", config => { config.WithAllowAdmin() .WithDatabase(0) .WithEndpoint("localhost", 6379); }) .WithMaxRetries(100) .WithRetryTimeout(50) .WithRedisBackplane("redis") .WithRedisCacheHandle("redis", true) ; });
其他的使用沒有任何變化,我們同時增加一些測試數據方便我們查閱對應的緩存數據。
/// <summary> /// 測試加入幾個不同的數據 /// </summary> /// <returns></returns> public void TestCache() { manager.Put("string", "abcdefg"); manager.Put("int", 2016); manager.Put("decimal", 2016.9M); manager.Put("date", DateTime.Now); manager.Put("object", new UserInfo { ID = "123", Name = "Test", Age = 35 }); }
private void btnTestSimple_Click(object sender, EventArgs e) { var list = Customer.Instance.GetAll(); Debug.WriteLine("客戶端獲取記錄數:{0}", list != null ? list.Count : 0); //測試加入一些值 Customer.Instance.TestCache(); }
我們其中測試,一切和原來沒有什麼差異,程序的記錄信息正常。
但是我們配置使用了Redis的緩存處理,因此可以使用“Redis Desktop Manager”軟件來查看對應的緩存數據的,打開軟件我們可以看到對應的緩存記錄如下所示。
從上圖我們可以查看到,我們添加的所有緩存鍵值都可以通過這個Redis的客戶來進行查看,因為我們緩存裡面有基於Redis緩存的實現,同理如果我們配置其他的緩存實現,如MemCache等,那麼也可以在對應的管理界面上查看到。
我們完成這些處理後,可以發現緩存數據是可以實現多層緩存的,最為高效的就是內存緩存(也是它的主緩存),它會自動協同好各個分布式緩存的數據版本沖突問題。
引入如Redis的分布式緩存有一個好處,就是我們的數據可以在程序重新啟動的時候,如果沒有在內存緩存裡面找到(沒有擊中目標),那麼會尋找分布式緩存並進行加載,從而即使程序重啟,我們之前的緩存數據依舊保存完好。
以上就是我基於對緩存框架的整體了解和其角色扮演做的相關介紹,以及介紹CacheManager的使用和一些場景的說明,通過上面簡單案例的研究,我們可以逐步引入到更具實際價值的Web API 框架層面上進行使用,以期把緩存框架發揮其真正強大的價值,同時也為我們各種不同的緩存需要進行更高層次的探索,希望大家繼續支持。