關於Orchard中的Caching組件已經有一些文章做了介紹,為了系列的完整性會再次對Caching組件進行一次介紹。
緩存的使用
在Orchard看到如下一段代碼:
可以看到使用緩存的方法Get而看不到以前常見的緩存方法:Add\Set\Remove,是不是很神奇。
其實也不是那么的神奇,Get方法中根據傳入的Key(culture)在緩存數據存儲中搜索,如果存在則直接返回緩存結果,如果不存在則執行傳入的委托,並把委托返回的結果放入到緩存中。
那么怎么確保緩存是不是失效呢?難道不支持嗎?
Orchard的緩存中比較難理解的就是這一塊了,Orchard的緩存失效機制非常的贊。
ctx.Monitor(_signals.When("culturesChanged"));
這一塊就是緩存失效的精髓,這邊暫把它叫做添加一個可監控的揮發令牌。這個When方法其實返回了一個實現了IVolatileToken接口的對象實例。下面會有詳細的說明。
接口關系
下面是緩存的基本接口的依賴關系
ICache<TKey,TResult>
ICache<TKey,TResult>是一個泛型接口,根據泛型名稱可以看出通過該接口可以定義緩存Key的類型和緩存結果的類型,也就是說Orchard中緩存的Key不只是大家所常見String類型。
ICache接口是緩存組件中最終的接口,可以這么理解ICacheManager、ICacheHolder都是緩存組件為了可擴展性和易用性而設立的抽象,ICache才是緩存的實現(存儲緩存數據的地方)。
下面我們來看一看它的實現類型:Cache<TKey,TResult>。
IVolatileToken
一個抽象的揮發令牌是緩存狀態的一個關鍵接口,里面的IsCurrent含義是是否是當前的對象,如果為false則代表緩存失效,如果為true則代表緩存有效。這邊大家先了解下概念后面會詳細說明。
字段
ICacheContextAccessor _cacheContextAccessor:緩存上下文訪問器。
ConcurrentDictionary<TKey, CacheEntry> _entries:線程安全的字典表,用於存儲緩存的數據。
類
CacheEntry 緩存條目,對緩存結果進行了封裝,主要對緩存結果添加了令牌機制(IVolatileToken)。
TResult Result:緩存的數據。
IEnumerable<IVolatileToken> Tokens:這個緩存條目所對應的揮發令牌(一個緩存可由多種情況導致緩存失效,如:5分鍾之后失效、數據被更改時候失效等多種失效方式)。
AddToken(IVolatileToken volatileToken):添加一個新的令牌至Tokens。
CompactTokens():主要用於去除Tokens中重復的令牌。(因為令牌是提供給外部添加的所有可能會出現重復的令牌,為提高性能(令牌內的執行執行時間不得而知)需要剔除重復的令牌)
接口方法
可以看到簡單的Cache類中包含了添加緩存、更新緩存的方法,而Get方法就是對這兩個方法的封裝,我們來看看Get方法的實現
代碼很簡單,使用了ConcurrentDictionary字典的特性添加或者更新(當Key不存在時執行第一個委托內的方法:AddEntrty,當key存在時執行第二個委托內的方法UpdateEntry)
私有方法
PropagateTokens
可以看作探測緩存是否失效。CreateEntry
AddEntry
UpdateEntry

注意看(currentEntry.Tokens.Any(t => t != null && !t.IsCurrent))緩存失效的核心就在這里了,實現了IVolatileToken接口的對象是一個引用類型,只要這個實例被添加至對應的緩存條目,並且通過一些手段將IsCurrent設為False那么這個緩存就失效了。
ICacheHolder(生命周期:租戶單例)
顧名思義這個接口主要是用來維護一個ICache接口集合的。
ICacheHolder中包含了一個方法
對應的實現
為什么需要ICacheHolder?
因為緩存Key是很容易沖突的,比如一名開發人員在開發管理員模塊的時將所有的管理員賬戶信息緩存起來那么緩存Key很可能為Users,那么另外一名開發人員在開發會員模塊時候也很有可能會使用Users這個人見人愛的Key,這時候緩存中的數據就沖突了。不過在Orchard中很好的避免了這個問題,就是分區。
在ICacheHolder中維護了一個ConcurrentDictionary<CacheKey, object>字典表,CacheKey為一個三元組,類型全是Type,分別為:使用緩存的組件類型,緩存Key類型,緩存結果類型。
以使用緩存的服務類、Key類型、結果類型作為一個Key,也就是說開發人員只要保證在同一個類型中不使用相同類型相同值的Key和相同的結果類型,該緩存就不會沖突(筆者覺得這個創意非常的贊)。
ICacheManager(生命周期:瞬態)
緩存管理者接口,也是大家最經常使用的接口。(ICacheManager的注入在上一篇“Orchard 刨析:前奏曲”中有解釋。)
字段
Type _component:使用緩存的服務組件類型。
ICacheHolder _cacheHolder:緩存持有者。
方法
ICache<TKey, TResult> GetCache<TKey, TResult>():從緩存持有者中獲取一個緩存。
TResult Get<TKey, TResult>(TKey key, Func<AcquireContext<TKey>, TResult> acquire):調用GetCache<TKey,TResult>方法,並且調用ICache的Get方法。
也包含了兩個方法,主要的方法是GetCache這一個,Get方法只是對這個方法進行了封裝,筆者覺得這種還是使用擴展方法來實現會更好。
下面我們來看看實現。
非常的簡單。
拿ISignals(生命周期:全局單例)開刀
ISignals是一個以信號量方式提供的一個簡單的揮發提供者。
ISignals接口提供了兩個方法一個用來生成Token,一個用來使Token失效(設置IsCurrent為false)。
我們來看內部實現
字段
IDictionary
方法
void Trigger<T>(T signal); 觸發一個信號量(設置IsCurrent為false導致緩存失效)。
IVolatileToken When<T>(T signal);(生成一個令牌)
可以看到這么一個簡單的類就可以實現一個緩存失效機制,可見Orchard的緩存失效機制是易於擴展的,Orchard中還內置了一些其他的失效機制實現,如:時間、文件監控等。
總結
以上是對Orchard緩存的一個簡單說明,一個簡簡單單的緩存都被Orchard設計的這么華麗,不得不敬佩Microsoft的工程師們,下面我們在簡單的過一遍緩存的流程。
異步令牌提供者
IAsyncTokenProvider
在上面也說了為什么需要壓縮緩存條目中的令牌,原因就是會影響性能,所以Orchard也提供了異步的令牌機制,主要是以異步的方式傳播令牌,實現方式也非常簡單,用到了線程池。
並行緩存
並行緩存暫時不提供說明,因為還有一些爭議:並行緩存是不是屬於緩存。
寫在最后
剛開始寫這個系列沒多久,可能開頭的文章邏輯並不是很清楚,期待后續文章的改善,如果在讀文中有遇到問題請移步QQ群:299744835,專為本系列提供的一個交流探討的地方。

















