前面的文章已經談了roller是如何生成頁面的,其實頁面就是一些HTML代碼。在roller中,使用一個類CachedContent對象來包裝這些HTML代碼,然後才把CachedContent對象放到緩存中以便以後使用。
談到緩存,roller的開發者們絕不會放過任何一個提高性能的機會。首先利用瀏覽器的緩存功能,見如下代碼:
if (ModDateHeaderUtil.respondIfNotModified(request, response, lastModified)) {
return;
} else {
// set last-modified date
ModDateHeaderUtil.setLastModifiedHeader(response, lastModified);
}
接下來談談roller如何爲了提高性能如何利用內存來實現緩存的。這裏說的主要針對博客頁面的緩存處理。爲了敘述方便,下面的是筆者根據源代碼整理的類圖:
在圖中,有兩個單例類,分別是CacheManager和WeblogPageCache。其中WeblogPageCache有一個Cache屬性,在實例化時通過CacheManager實例獲取一個Cache對象。
而CacheManager實例化時要通過讀取屬性文件來初始化cacheFactory屬性,默認情況下cacheFactory是一個ExpiringLRUCacheFactoryImpl對象。這裏應用了工廠模式。
通過圖中我們可以知道WeblogPageCache的contentCache屬性實際是一個ExpiringLRUCacheImpl對象。
我們知道緩存很好用,put進去get出來。看起來簡單,但真正的緩存系統要做很多工作,比如是否過期了,如何清理緩存等等。
Roller中定義了一個Cache接口,代表一個緩存。有兩個實現類,分別是LRUCacheImpl和ExpiringLRUCacheImpl,他們是父子關係。實現了LRU算法的緩存策略。作爲緩存接口自然少不了這幾個重要的方法:put、get、remove、clear。
WeblogPageCache類爲我們屏蔽了很多實現細節,如果是作爲緩存的使用者,只要瞭解WeblogPageCache的API就可以了。WeblogPageCache同樣也有和Cache類似的方法,這些對應的方法其實是把請求委派給Cache來處理。WeblogPageCache還有一個重要的方法generateKey,生產key很重要,但筆者不打算在這裏講。
通過類圖和前面的文字介紹,我們可以知道系統是如何準備好緩存等待我們使用的了。
前面提到html被包裝到CachedContent對象中,當頁面需要緩存時,WeblogPageCache的put方法被調用,傳入一個key和CachedContent對象。同時new一個LazyExpiringCacheEntry對象來包裝CachedContent。爲什麼這樣做呢?目的是爲了在get時判斷數據是否有效。
先說說本人對緩存的兩個概念(有效和過期)的理解。有效是指被緩存的數據已經被修改過了;過期是指當被緩存的數據超過了緩存系統設定的時間長度。
WeblogPageCache在put時,其實是把請求委派給Cache對象的,而這個Cache就是一個ExpiringLRUCacheImpl對象。ExpiringLRUCacheImpl的put方法被調用時,又new了一個ExpiringCacheEntry對象來包裝LazyExpiringCacheEntry對象。這樣做的目的是爲了在get時判斷被緩存數據是否過期。
緩存的put過程就是這樣的,下面說說如何從緩存中取出數據,取出數據是通過調用get方法。
當想獲取一個緩存數據時需要調用WeblogPageCache的get方法,此方法需要傳入一個key和一個博客最近修改時間。Get的過程不是很好描述。WeblogPageCache把請求傳給Cache對象上的get方法。在Cache的get方法中通過key從Map中取出value,這個value先是一個ExpiringCacheEntry對象,因爲此對象被new時初始化了超時時間和緩存時間。所以通過
public boolean hasExpired() {
long now = System.currentTimeMillis();
return ((this.timeCached + this.timeout) < now);
}
可以判斷一個數據是否過期。如果過期就被從緩存中刪除了,否則就從ExpiringCacheEntry中取出value(也就是LazyExpiringCacheEntry)返回。
一個數據不過期並不代表能使用,還要判斷他是否有效。程序控制權回到WeblogPageCache的get方法。如果Cache的get方法不是null,就要判斷是否有效。LazyExpiringCacheEntry對象被new出來時,它的被緩存時間也同時被初始化。由於它是包裝CachedContent對象的,因此它也有個get方法,參數是最近修改時間。在此方法中,通過判斷最近時間是否大於緩存時間來確定數據是否有效。
如果數據是有效的就把CachedContent返回,否則就返回null。整個緩存的get過程就這樣結束了。
體會:roller的緩存用了工廠模式,這樣的好處是:當我們的站點規模變大時,roller緩存可能滿足不了要求,這時如果使用優秀的緩存系統(比如memcached)是比較容易集成到roller中的。
歡迎大家一起討論roller。。。。。。