寫自己的緩存框架,JAD-CACHE架構設計篇
在之前一篇《寫一個自己的通用緩存框架,以同時支持ehcache、mecache以及springcache注解等等》博文中,列出了自己的通用緩存框架需要實現在的大致功能總結如下:
1、提供統一的緩存操作api;
2、支持同時使用多種緩存實現;
3、提供靈活的配置;
4、需要防止緩存穿透;
5、需要可以靈活指定緩存存活時間;
6、需要任意控制緩存的停用或啟用。
目前這個框架的編碼部分已完成,并取名為JAD-CACHE,取這個名字的原因是因為它是我的個人的JAD項目的一部分,JAD項目是本人用業余時間開發一個企業基礎架構平臺,因涉及的東西比較多,而且很多模塊還沒有完全完成。目前準備把其中做的比較完善的緩存模塊單獨從原項目中剝離出來作為一個獨立的項目并準備開源給大家測試和使用,于是也就有了JAD-CACHE。
本文先展示這個框架的原理及架構設計,后續開放源代碼后再發布一些使用手冊方面的文章。
JAD-CACHE緩存框架是在spring cache模塊的基礎上擴展而來,在上一篇博文《通用緩存框架,spring緩存模塊原理分析篇》已經系統分析過srping cache的原理,這里不再重復。spring cache模塊重要的兩個類就是org.springframework.cache.Cache和 org.springframework.cache.CacheManager。現在我跟據自己的需要對它們進行擴展。
我設計的Cahce相關的擴展類圖如下:
圖:Cahce擴展類類圖1
上圖中灰色部分是spring自已的類,其它的是擴展出來的。上圖ManageredCache是一個接口,表示這個Cache可由本框架的CacheClient實例管理起來(關于CacheClient的概念稍后介紹)。ManageredCache接口在父接口Cache的基礎上,增加了isAllowNullValues()等等方法,作用分別如下:
getCacheClient()
獲得管理它的CacheClient實例
isAllowNullValues()
能否在此緩存中保存null值,防止緩存穿透
getActivityTime()
獲得此緩存中對像存活時間,注意這個存活時間是一個業務存活時間,開發人員可通過配置指定一個默認的時間,也可以在調用put()方法緩存對像時通過參數另外指定一個存活時間。這樣,在調用get()方法從緩存中取出對像后,會先通過個這個方法判斷對像是否失效(即使它依舊存在于緩存中,但我們可以在業務的角度上以為它失效了),這個存活時間應該短于用戶在ehcache.xml等配置文件配置的存活時間,這樣就實現了個性化指定同一類型不同對像的存活時間。
put(Object, Object,int)
這個方法跟父類Cache接口的put(Object,Object)功能相同,就是把對像緩存起來,但它支持一個int類型的參數,用于指定對像存活時間的秒數。
size()
統計對像總數
keySet()
獲得所有key
所有要緩存的數據都不是直接持久化到緩存容器中的,而是被裝包成了一個個CacheValue類型的實例,在上圖的類圖中,可以看到,CacheValue類包含兩個屬性expiryTime和value,其中expiryTime是超時時間,value就是據體的數據對像。如果數據對像為null值,就轉換成NullCacheData實例。在AbstractManageredCache這個抽象類的相關方法中,就實現了這些邏輯。
AbstractManageredCache是對ManageredCache接口的抽象實現,實現了在操作緩存之前,先通過管理它的CacheClient實例判斷當前緩存客戶端的狀態是否已開啟 (調用cacheClient的isStarted()方法),如果啟用,就調用父接口Cache中聲明的getNativeCache()獲得具體的緩存實例操作緩存,如果沒有啟用,就什么也不做。同時,在它的put()方法實現過程中,會跟據它的activityTime屬性指定存活時間,或跟據allowNullValues屬性決定是否緩存null值,并將所有要緩存的數據包裝成CacheValue實例持久化到緩存容器中。而在get()方法實現的羅輯,也會做相應的操作,跟據activityTime屬性踢出超時的對像,并將緩存容器中的CacheValue實例轉換成原始的數據類型。
在spring原來的緩存模型中,所有的Cache實例都被ManagerCache所管理。但本框架抽像出了一個叫CacheClient的概念,所有的緩存實例都被擴展成了ManageredCache對像,并被一個CacheClient實例管理起來(而此CacheClient實例又持有一個CacheManager的引用,這樣一來Cache也就可以通過CacheClient間接的被ManagerCache管理)。這樣做有一個好處就是:在Cache操作緩存之前,先通過它的CacheClient判斷當前的緩存狀態,跟據這個裝態決定是否要進行操作。設計CacheClinet還有一個目地,就是讓多個緩存實現能更好的共存于同一個應用之中,比如讓EhCache實現的緩存交給一個CacheClinet管理,讓MemCache實現的緩存交給另一個CacheClinet管理。在spring原來的緩存模塊中,設計了一個叫CompositeCacheManager的類,可以同時配置多個CacheManager實例以達到這個目地。但我棄用它,改用CacheClinet的目地,就是想增加可以任意停用或啟用某些Cache的功能。比如,當memcache服務器掛掉時,我們通過它對應的CacheClinet實例改變這個實例管理的所有Cache的狀態,停用它,從而達到從應用層上禁用或啟用緩存的目地。CacheClient相關的類圖如下:
圖:CacheClient相關類圖
上圖中的CacheClient類是一個接口,它聲明的一些諸如start(),stop()等方法,用于修改本實例的狀態,啟用或停用,而isStarted()就是用來檢查狀態的,返回當前Client是否啟用。每一個CacheClient實例管理一個CacheManager,通過該接口中的getCacheManager()可以獲得它所控制的CacheManager實例引用,每個一個實例有一個***的名字,通過getClientName()可以獲取它的名字。除此外,此接口還有一些諸如getAllowNullValues(),setDefActivityTime()之類的方法。這是為了方便開發人員對緩存的配置,開發人員在配置CacheClient實例時,可以在這里配置allowNullValues, defActivityTime等屬性,這樣再在配置CacheManager或者Cache實例時就可以不指定了這些屬性了,Cache會自動繼承它的Client的屬性值。這些配置在CacheClient接口的抽像實現類AbstractCacheClient中都有相應的實現。
在AbstractCacheClient中還有一個重要的屬性,就是autoStart屬性。這個屬性如果配置為true,那在spring容器初始化的時候,這個Client實例一但生成,就會自動調用它的start()啟動它,以使得它所控制的CacheManager可以正常操作緩存。否則,它不會自動啟動,它所控制的所有Cache都處于禁用狀態。
另外,為了方便配置,在AbstractCacheClient還提供了autoCreateCache屬性,用于指定此客戶端能否自動創建緩存實例。在Spring原來的緩存配置中,需要把用到的每個Cache都寫到配置文件中,要么配到ehcache.xml中,要么在配置CacheManager時附加Cache實列相關的配置。否則,在操作沒有配置的緩存時,會提示找不到某某名稱的cache。在本框架中,如果指定了autoCreateCache屬性為true,在調用CacheManager.getCache(String name)獲取不到Cache時,會自動創建一個默認的(通過覆蓋CacheManager.getMissgeCache()實現)。當然,如果指定autoCreateCache為false時,就不會自動創建,這要求用戶在ehcache.xml中自己配置。當然,為了方便,本框架,還可以直接在CacheClient中配置,上面的類圖AbstractCacheClient中兩個屬性cacheNames和cacheBeans就是用于這個配置的。用戶可通過cacheNames只配置名稱,或者通過cacheBeans屬性配置一個類型為的JadCache實例bean。這個JadCache類似于spring 在實現ehcache時提供的EhCacheFactoryBean。只不過這個更加簡潔通用。如果只通過cacheNames配置一個Cache的名稱,那么此Cache實例的相關屬性都采用默認值這要求用戶在ehcache.xml配置文件中配置一個defaultCache,或需要在memcache.xml中配置一個默認的cacheclient。
每一個CacheClient***管理一個CacheManager實例(在上面類圖中可以看到,AbstractCacheClient類中有一個JadCacheManager類型的屬性:cacheManager。關于本框架的CacheManager擴展稍后介紹)。CacheClient在被spring初始化時,會自動調用registryCacheManager()方法,跟據不同的緩存實現廠商生成一個對應的CacheManager實例,并注冊到spring context中,同時調用initCache()方法通過配置中的cacheNames或cacheBeans自動初始化所有的Cache實例。初始化完成后,再調用registryToMasterCacheManager()方法,這個方法稍后介紹。
一個應用系統中,可以有一個或多個CacheClient實例,每個CacheClient實例控制一個CacheManger。開發人員通過配置不同的CacheClient實例可以實現同時支持多個不同的緩存實現,比如,把EhCache相關的緩存配置到一個CacheClient中而把MemCache配置到另一個CacheClient中。
為了統一管理所有CacheClient實例,本框架設計了一個叫CacheClientManager來管理所CacheClient實例,它有一個抽像實現AbstractCacheClientManager,這個抽像類中,也有allowNullValues, defActivityTime等CacheClient中具的相同的屬性,只不過這里的屬性作為一個全局的配置,使得CacheClient可以省去這些配置而直接使用CacheClientManager中的配置。整個應用中,只能有CacheClientManager實例,但這個實例可以管理一個或多個CacheClient。而通常況下,一些簡單的應用系統中,往往只有一種緩存實現,也就是只需要配置一個CacheClient,因此AbstractCacheClientManager給出了兩個不同的實現類,分別是上圖中紅顏色的SingleClientManager和MultiClientManager,分別表示單CacheClient管理或多CacheClient管理器。開發人員可跟據業務情況選擇性使用其中一個進行配置。
前文提到,每個CacheClient在初始化時,會自動生成一個對應的CacheManager實例并注冊到Spring上下文中,但Spring在操作緩存時,為了能準確的通過CacheManager找到相應名稱的Cache實例,這就要求還需要對這些CacheManager實例進行統一管理。在spring原來的緩存模塊中,提供了一個叫CompositeCacheManager的實現類,以組合設計模式的方式來管理這些CacheManager實例,這個CompositeCacheManager實現類有一個列表指向所有CacheManager實例的引用。在執行getCache(String)時會遍歷這個列表,循環調用每個實例的getCache(String)方法,然后返回一個不為null的Cache,但是如果所有的CacheManager都獲取不到Cache時,這個方法最終是會返回null的。然而,在本框架中,是支持找不到Cache時自動創建的,所以在本框架中,我設計了一個叫MasterCacheManager的類來管理這些CacheManager,同時,這個類有一個叫defCacheManager的CacheManager屬性來指定一個默認的CacheManager。在跟據名稱找不到任何Cache時,就自動調用這個默認CacheManager的addCache()方法來自動創建一個。本框架CacheManager相關的類圖如下所示:
圖:CacheManager類圖
上圖灰色總分是spring緩存模塊自帶的類,其它顏色是擴展類。其中最右邊的MasterCacheManager類就是上文提到管理所有CacheManager的實現類。上圖左邊的JadCacheManager是一個接口,提供initCache(JadCache)和newCache(JadCache)兩個方法,這兩個方法,主要是用于在CacheClient初始化過程自動生成CacheManager實現類后,準備通過initCache()方法來初始化配置中的Cache來調用的。對于MasterCacheManager類的實例化,開發人員無需額外在spring context中配置,因為在CacheClientManager的初始化過程中,會自動注冊一個MasterCacheManager類型的實例到spring context中(上面類圖AbstractCacheClientManager這個類中的registerMasterManager()方法就是用來做這個事的)。而這個類中cacheManagerMap的屬性就是按名稱組織起來的CacheManager類型的集合。在每個CacheClient初始化時自動生成CacheManager實例后,會調用CacheClient類的registryToMasterCacheManager()方法,這個方法會從當前Spring conetxt中獲取到MasterCacheManager實例,然后調用它的register()方法,把它注冊到MasterCacheManager實例中(添加到cacheManagerMap集合)。
上面類圖中的JadAbstractCacheManger是借用了Srping的AbstractCacheManger對JadCacheManager的一個抽象實現,后面在集成EhCache或MemCache等所有緩存時就從這個類往下擴展。
以上就是本框架的一個總體設計,因為篇附過長。后面再在其它的文章中給出在這個基礎上擴展EhCache和MemCache的方案,并實現一個基于Map的內存緩存實現。想關注更多信息或者想及時了解動態的同學們可以掃以下二維碼關注我的微信公眾號,多謝