Orchard 刨析:Caching

關於Orchard中的Caching組件已經有一些文章作了介紹,爲了系列的完整性會再次對Caching組件進行一次介紹。html

緩存的使用

在Orchard看到以下一段代碼:緩存

image

能夠看到使用緩存的方法Get而看不到之前常見的緩存方法:Add\Set\Remove,是否是很神奇。安全

其實也不是那麼的神奇,Get方法中根據傳入的Key(culture)在緩存數據存儲中搜索,若是存在則直接返回緩存結果,若是不存在則執行傳入的委託,並把委託返回的結果放入到緩存中。異步

那麼怎麼確保緩存是否是失效呢?難道不支持嗎?ide

Orchard的緩存中比較難理解的就是這一塊了,Orchard的緩存失效機制很是的贊。性能

ctx.Monitor(_signals.When("culturesChanged"));
這一塊就是緩存失效的精髓,這邊暫把它叫作添加一個可監控的揮發令牌。這個When方法其實返回了一個實現了IVolatileToken接口的對象實例。下面會有詳細的說明。

接口關係

下面是緩存的基本接口的依賴關係ui

image

ICache<TKey,TResult>

ICache<TKey,TResult>是一個泛型接口,根據泛型名稱能夠看出經過該接口能夠定義緩存Key的類型和緩存結果的類型,也就是說Orchard中緩存的Key不僅是你們所常見String類型spa

ICache接口是緩存組件中最終的接口,能夠這麼理解ICacheManagerICacheHolder都是緩存組件爲了可擴展性和易用性而設立的抽象,ICache纔是緩存的實現(存儲緩存數據的地方)線程

下面咱們來看一看它的實現類型:Cache<TKey,TResult>設計

image

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方法的實現

image

代碼很簡單,使用了ConcurrentDictionary字典的特性添加或者更新(當Key不存在時執行第一個委託內的方法:AddEntrty,當key存在時執行第二個委託內的方法UpdateEntry)

私有方法

PropagateTokens

image

能夠看做探測緩存是否失效。

CreateEntry

image

AddEntry

image

UpdateEntry

image
注意看(currentEntry.Tokens.Any(t => t != null && !t.IsCurrent))緩存失效的核心就在這裏了,實現了IVolatileToken接口的對象是一個引用類型,只要這個實例被添加至對應的緩存條目,而且經過一些手段將IsCurrent設爲False那麼這個緩存就失效了。

ICacheHolder(生命週期:租戶單例)

顧名思義這個接口主要是用來維護一個ICache接口集合的。

ICacheHolder中包含了一個方法

image

對應的實現

image

爲何須要ICacheHolder?

由於緩存Key是很容易衝突的,好比一名開發人員在開發管理員模塊的時將全部的管理員帳戶信息緩存起來那麼緩存Key極可能爲Users,那麼另一名開發人員在開發會員模塊時候也頗有可能會使用Users這我的見人愛的Key,這時候緩存中的數據就衝突了。不過在Orchard中很好的避免了這個問題,就是分區。

在ICacheHolder中維護了一個ConcurrentDictionary<CacheKey, object>字典表,CacheKey爲一個三元組,類型全是Type,分別爲:使用緩存的組件類型,緩存Key類型,緩存結果類型

以使用緩存的服務類、Key類型、結果類型做爲一個Key,也就是說開發人員只要保證在同一個類型中不使用相同類型相同值的Key和相同的結果類型,該緩存就不會衝突(筆者以爲這個創意很是的贊)。

ICacheManager(生命週期:瞬態)

image

緩存管理者接口,也是你們最常用的接口。(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方法只是對這個方法進行了封裝,筆者以爲這種仍是使用擴展方法來實現會更好。

下面咱們來看看實現。

image

很是的簡單。

拿ISignals(生命週期:全局單例)開刀

ISignals是一個以信號量方式提供的一個簡單的揮發提供者。

image

ISignals接口提供了兩個方法一個用來生成Token,一個用來使Token失效(設置IsCurrent爲false)。

咱們來看內部實現

image

字段

IDictionary < object, Token> _tokens:用來存儲信號量令牌的字典表。

方法

void Trigger<T>(T signal); 觸發一個信號量(設置IsCurrent爲false致使緩存失效)。
IVolatileToken When<T>(T signal);(生成一個令牌)

image

image

能夠看到這麼一個簡單的類就能夠實現一個緩存失效機制,可見Orchard的緩存失效機制是易於擴展的,Orchard中還內置了一些其餘的失效機制實現,如:時間、文件監控等。

總結

以上是對Orchard緩存的一個簡單說明,一個簡簡單單的緩存都被Orchard設計的這麼華麗,不得不敬佩Microsoft的工程師們,下面咱們在簡單的過一遍緩存的流程。

image

異步令牌提供者

IAsyncTokenProvider

image

在上面也說了爲何須要壓縮緩存條目中的令牌,緣由就是會影響性能,因此Orchard也提供了異步的令牌機制,主要是以異步的方式傳播令牌,實現方式也很是簡單,用到了線程池。

並行緩存

並行緩存暫時不提供說明,由於還有一些爭議:並行緩存是否是屬於緩存。

寫在最後

剛開始寫這個系列沒多久,可能開頭的文章邏輯並非很清楚,期待後續文章的改善,若是在讀文中有遇到問題請移步QQ羣:299744835,專爲本系列提供的一個交流探討的地方。

相關文章
相關標籤/搜索