摘要
本文檔介紹了如何在
Portlet
應用中使用
OSCache
進行頁面內容緩存和對象緩存,分析了
OSCache
的實現原理,并提出了一個用來緩存對象和頁面的簡單易用的方案。本文檔不涉及
OSCache
的安裝與配置的內容,相關內容請參考
OSCache
的
在線文檔
。
OSCache標記庫由OpenSymphony設計,它是一種開創性的緩存方案,它提供了在現有JSP頁面之內實現內存緩存的功能。OSCache是個一個被廣泛采用的高性能的J2EE緩存框架,OSCache還能應用于任何Java應用程序的普通的緩存解決方案。
OSCache有以下特點:
l?????
緩存任何對象:你可以不受限制的緩存部分jsp頁面或HTTP請求,任何java對象都可以緩存。
l?????
擁有全面的API:OSCache API允許你通過編程的方式來控制所有的OSCache特性。
l?????
永久緩存:緩存能被配置寫入硬盤,因此允許在應用服務器的多次生命周期間緩存創建開銷昂貴的數據。
l?????
支持集群:集群緩存數據能被單個的進行參數配置,不需要修改代碼。
l?????
緩存過期:你可以有最大限度的控制緩存對象的過期,包括可插入式的刷新策略(如果默認性能不能滿足需要時)。
圖
3-1 OSCache
架構概覽
Cache Factory
:該實體負責獲得
Cache Proxy
,兼有一些對
Cache Proxy
的管理功能。對應到現在的
OSCache
實現中的類是:
GeneralCacheAdministrator
和
ServletCacheAdministrator
。
Cache Proxy
:該實體是
Cache Map
的代理,它主要負責從
Cache Map
中取得
/
存儲指定的緩存對象,如果緩存對象過期,那么就將緩存刷新,并向指定的監聽者發送存
/
取事件。對應到現在的
OSCache
實現中的類是:
Cache
和
ServletCache
。
Cache Map
:該實體存儲了所有的緩存實體,是一個
OSCache
專有的
Map
實現,它能根據指定的算法清除緩存,以及將緩存持久化到磁盤中。對應到現在的
OSCache
實現中的類是:
FIFOCache
,
LRUCache
和
UnlimitedCache
。
Listeners
:
OSCache
存
/
取事件的監聽者實體。對應到現在的
OSCache
實現中的類是:
CacheEntryEventListener
和
CacheMapAccessEventListener
。
Cache Entry
:表示緩存對象的包裝實體,它包裝了緩存對象和刷新策略。對應到現在的
OSCache
實現中的類是:
CacheEntry
。
一個典型的“緩存對象”場景是:
應用調用
Cache Factory
獲得
Cache Proxy
,然后應用將要緩存的對象以及刷新策略通過
Cache Proxy
存儲到
Cache Map
中,并通知各個
Listener
。
一個典型的“取得緩存對象”的場景是:
應用調用
Cache Factory
獲得
Cache Proxy
,然后給
Cache Proxy
的相應方法傳入要獲得的緩存對象的
key
,
Cache Proxy
會根據指定的刷新策略判斷緩存是否過期,如果緩存沒有過期,則返回緩存對象,如果緩存過期,則刷新緩存,并向應用層拋出需要刷新的異常(
NeedsRefreshException
),應用如果收到此異常,將重新計算內容并將內容緩存。
?
OSCache
現有的緩存刷新策略(超過指定時間后自動過期,超過指定日期后自動過期,按照克龍表達式的設定自動過期)不能滿足需求。現有應用要求:當數據源的部分內容更新后,能夠使相關緩存過期,展現頁面從數據源取得更新的內容顯示。
該方案為下列場景提供了支持:
1.
緩存整個
Response
,能夠定制刷新指定的
Response
。
2.
緩存
JSP
頁面內容,能夠定制刷新指定的緩存內容。
3.
緩存對象,能夠定制刷新指定的緩存對象。
在
Web
應用的
web.xml
中添加下面的內容:
<
filter>
?????????????
<
filter-name>CacheFilter</filter-name>
?????????????
<
filter-class>
???????????????????? com.***.portal.oscache.***CacheFilter
?????????????
</
filter-class>
?????????????
<
init-param>
????????????????????
<
param-name>time</param-name>
????????????????????
<
param-value>-1</param-value>
?????????????
</
init-param>
?????????????
<
init-param>
????????????????????
<
param-name>scope</param-name>
定制這個
fileter
緩存的
response
的組
|
????????????????????
<
param-value>application</param-value>
?????????????
</
init-param>
?????????????
<init-param>
???????????????????? <param-name>groups</param-name>
???????????????????? <param-value>
landy
</param-value>
????????????? </init-param>
??????
</
filter>
<
filter-mapping>
?????????????
<
filter-name>CacheFilter</filter-name>
?????????????
<
url-pattern>/
landy
</
url-pattern>
??????
</
filter-mapping>
|
注意:
為了在部分數據更新時只刷新部分
Response
,
***CacheFilter
比
OSCache
提供的
CacheFitler
增加了一個可配置的參數:“
groups
”,部署者可以通過這個參數配置這個
filter
所映射的
URLS
的組(通常同一個組的
URL
會從同時更新的數據源取數據),這樣當數據源某部分更新之后,我們就可以刷新從這部分數據源取數據的
URLs
。該配置項支持配置多個組,以逗號作為分隔符,如:“
group1,group2
”。
其他的配置項說明請參考
OSCache
在線文檔的
CacheFilter
配置部分
。
OSCache
提供的
CacheFilter
能夠緩存
Response
,但不能供應用選擇性的刷新某些
Response
,而
***CacheFilter
就能支持這一特性。
您可以按照以下的方式刷新某一組(
Filter
初始化參數中
groups
參數配置的內容)的
Responses:
ServletCacheAdministrator admin = null;
Filter
初始化參數中
scope
參數配置的內容
?
|
admin = ServletCacheAdministrator.getInstance(config
?????????????????????????? .getServletContext());
Filter
初始化參數中
groups
參數配置的內容
|
Cache cache = admin.getCache(httpRequest, cacheScope);
cache. flushGroup(group);
|
這種方案僅適用于普通的
web
應用,不適用于
Portlets
應用,因為在
Portlets
應用中,每個
Portlet
都是頁面上一個可插拔的組件,如果被緩存的
Response
代表的頁面中某個
Portlet
被刪除了,那么這個頁面產生的
Response
就會不一樣,而這時應用卻無法刷新緩存的
Response
。
可以在
JSP
頁面中將要緩存的內容置于
<cache></cache>
標簽之間,當更新數據源的事件產生時,在處理事件的方法中加入如下代碼即可:
***OSCacheUtil.getInstance().flushGroup("group1");
|
我要緩存一個從數據源的
A
表取數據的
JSP
內容段,如下表所示:
<oscache:cache key="foobar" scope="application" time="-1" groups="group1" >
//business code
Select * from table A and Display
</oscache:cache>
|
數據源更新了,在處理數據更新事件的代碼中,我加入下表的代碼:
***OSCacheUtil.getInstance().flushGroup("group1");
|
這樣就可以刷新緩存的
group1
組的數據了。
本方案在滿足了
4.1
描述的需求的條件下,僅支持選擇性的刷新存儲在
application
域(
cache
標簽中
scope
屬性配置為
application
)的緩存。為什么不支持選擇性的刷新
session
域的緩存呢?是因為數據更新的事件產生是隨機的,當數據更新事件發生時,我們無法得到
web
服務器中的每個
session
,所以不能支持選擇性的刷新
session
域的緩存。
可以在
web
應用的業務處理邏輯中將要緩存的對象采用如下的方式將對象緩存到
application
域中:
***OSCacheUtil.getInstance().putInCache(key, content, new String[]{“group1”});
|
當更新數據源的事件產生時,在處理事件的方法中加入如下代碼即可:
***OSCacheUtil.getInstance().flushGroup("group1");
|
同
4.5.2
一致。
?
5????
開始使用OSCache中的緩存組件
OSCache中按照緩存范圍的不同分為兩種不同的方式:一種是緩存JSP頁面中部分或者全部內容,一種是基于整個頁面文件的緩存。
4.1?JSP部分內容緩存
4.1.1?Cache-OSCache提供的緩存標簽
這是OSCache提供的標簽庫中最重要的一個標簽,包括在標簽中的內容將應用緩存機制進行處理,處理的方式將取決于編程者對cache標簽屬性的設置。
第一次請求到達時,標簽中的內容被處理并且緩存起來,當下一個請求到達時,緩存系統會檢查這部分內容的緩存是否已經失效,主要是以下幾項:
- 1. 緩存時間超過了cache標簽設置的time或者duration屬性規定的超時時間
- 2. cron屬性規定的時間比緩存信息的開始時間更晚
- 3. 標簽中緩存的內容在緩存后又被重新刷新過
- 4. 其他緩存超期設定
如果符合上面四項中的任何一項,被緩存的內容視為已經失效,這時被緩存的內容將被重新處理并且返回處理過后的信息,如果被緩存的內容沒有失效,那么返回給用戶的將是緩存中的信息。
cache標簽的屬性說明:
key - 標識緩存內容的關鍵詞。在指定的作用范圍內必須是唯一的。默認的key是被訪問頁面的URI和后面的請求字符串。
你可以在同一個頁面中使用很多cache標簽而不指定他的key屬性,這種情況下系統使用該頁面的URI和后面的請求字符串,另外再自動給這些key增加一個索引值來區分這些緩存內容。但是不推薦采用這樣的方式。
scope - 緩存發生作用的范圍,可以是application或者session
time - 緩存內容的時間段,單位是秒,默認是3600秒,也就是一個小時,如果設定一個負值,那么這部分被緩存的內容將永遠不過期。
duration - 指定緩存內容失效的時間,是相對time的另一個選擇,可以使用簡單日期格式或者符合USO-8601的日期格式。如:duration='PT5M' duration='5s'等
refresh - false 或者true。
如果refresh屬性設置為true,不管其他的屬性是否符合條件,這部分被緩存的內容都將被更新,這給編程者一種選擇,決定什么時候必須刷新。
mode - 如果編程者不希望被緩存的內容增加到給用戶的響應中,可以設置mode屬性為"silent"
其它可用的屬性還包括:cron 、groups、language、refreshpolicyclass、refreshpolicyparam。
上面的這些屬性可以單獨使用,也可以根據需要組合使用,下面的例子將講解這些常用屬性的使用方式。
4.1.2?Cache標簽實例分析:
1. 最簡單的cache標簽用法
使用默認的關鍵字來標識cache內容,超時時間是默認的3600秒
<cache:cache>
<%
//自己的JSP代碼內容
%>
</cache:cache>
|
2. 用自己指定的字符串標識緩存內容,并且設定作用范圍為session。
<cache:cache key="foobar" scope="session">
<%
//自己的JSP代碼內容
%>
</cache:cache>
|
3.動態設定key值,使用自己指定的time屬性設定緩存內容的超時時間,使用動態refresh值決定是否強制內容刷新。
因為OSCache使用key值來標識緩存內容,使用相同的key值將會被認為使用相同的的緩存內容,所以使用動態的key值可以自由的根據不同的角色、不同的要求決定使用不同的緩存內容。
<cache:cache key="<%= product.getId() %>" time="1800" refresh="<%= needRefresh %>">
<%
//自己的JSP代碼內容
%>
</cache:cache>
|
4. 設置time屬性為負數使緩存內容永不過期
<cache:cache time="-1">
<%
//自己的JSP代碼內容
%>
|
5. 使用duration屬性設置超期時間
<cache:cache duration='PT5M'>
<%
//自己的JSP代碼內容
%>
|
6. 使用mode屬性使被緩存的內容不加入給客戶的響應中
<cache:cache mode='silent'>
<%
//自己的JSP代碼內容
%>
|
參考資料:
[1]
http:/www.opensymphony.com/oscache/
引自: http://tb.blog.csdn.net/TrackBack.aspx?PostId=715519