=====================================
apache tomcat中文件的緩存機制
=====================================
一. 環境
version: apache tomcat 5.5.26
二. 緩存機制[ServletContext.getResourceAsStream]
1. 根據給出的path到緩存中查詢CacheEntry; 以下是從ProxyDirContext類中摘出來的方法
protected CacheEntry cacheLookup(String name) {
if (cache == null)
return (null);
if (name == null)
name = "";
for (int i = 0; i < nonCacheable.length; i++) {
if (name.startsWith(nonCacheable[i])) {
return (null);
}
}
CacheEntry cacheEntry = cache.lookup(name);
if (cacheEntry == null) {
cacheEntry = new CacheEntry();
cacheEntry.name = name;
// Load entry
cacheLoad(cacheEntry);
//註釋:要特別注意cacheLoad這個方法, 它緩存文件最後修改時間時,並沒有取真正的時間,僅僅緩存了
//文件屬性對象[這時候的修改時間爲-1, 還沒有, 只有等到下次對比的時候才取]
} else {
if (!validate(cacheEntry)) {
if (!revalidate(cacheEntry)) {
//註釋: revalidate這個方法要注意,它是用文件的大小和最後修改時間來比較的
cacheUnload(cacheEntry.name);
return (null);
//註釋: 它清除緩存之後,並沒有馬上將新的內容加載到緩存中.
} else {
cacheEntry.timestamp =
System.currentTimeMillis() + cacheTTL;
}
}
cacheEntry.accessCount++;
}
return (cacheEntry);
}
protected void cacheLoad(CacheEntry entry) {
String name = entry.name;
// Retrieve missing info
boolean exists = true;
//註釋:出問題的點就在下面,它只將文件的屬性對象緩存了,
//並沒有取文件的最後修改時間[它的初始化值是-1, 只有調用
//之後,纔會取得真正的最後修改時間].
// Retrieving attributes
if (entry.attributes == null) {
try {
Attributes attributes = dirContext.getAttributes(entry.name);
if (!(attributes instanceof ResourceAttributes)) {
entry.attributes =
new ResourceAttributes(attributes);
} else {
entry.attributes = (ResourceAttributes) attributes;
}
} catch (NamingException e) {
exists = false;
}
}
....
}
2. 如果找到CacheEntry, 則將CacheEntry.resource返回;
如果沒有找到,則到文件系統加載指定的文件;
從以上的分析來看,它的緩存有以下幾個特點:
> 從緩存中獲取的時候,它是根據path獲取一個CacheEntry對象,如果有,還需要驗證這個對象是否有效,如果無效,就直接刪除;
> 刪除緩存的內容時,並不馬上將新的內容添加到緩存中,而只是簡單的將緩存清空而已;
> 建立緩存的時候,它將緩存對象[文件或資源]的屬性也緩存了[但當時並沒有取出最後修改時間]
> 判斷一個緩存對象是否有效的依據是:文件大小和文件的最後修改時間.
由於tomcat在生成緩存對象的時候,只是將緩存對象的屬性對象緩存起來,並沒有獲取"最後修改時間屬性", 所以導致下一次對比緩存的
時候,緩存中的"最後修改時間屬性"就是當前文件的最後修改時間.
例如:
[原始文件, 最後修改時間: 001]
第一次訪問: 緩存不存在, 生成緩存, 並生成緩存屬性[緩存屬性中的最後修改時間: -1]
[第一次修改文件, 最後修改時間: 002]
第二次訪問: 緩存存在, 取得緩存對象, 對比時間和大小, 由於緩存屬性的最後修改時間爲-1, 所以取當前文件的最後修改時間
假設文件大小沒有改變,所以就直接使用緩存中的內容[緩存屬性中的最後修改時間: 002]
[第二次修改文件, 最後修改時間: 003]
第三次訪問: 緩存存在,從緩存中取, 由於緩存中的最後修改時間與當前的最後修改時間不一樣,所以清緩存;
[第三次修改文件, 最後修改時間:004]
第四次訪問: 緩存不存在, 重複第一次訪問的邏輯
因此, 按以上邏輯, 第2, 5, 8, 11...(3*n-1, n爲自然數)次訪問的時候會出問題,沒有取到最新的內容,而是上一次的緩存結果.
這個問題的根據原因是:對時間屬性的緩存,並沒有緩存真正的時間(不知道tomcat出於什麼原因,爲什麼緩存的時候不將最後修改時間
取出來?)
三. 說明
1. CacheEntry上的關鍵屬性:
> resource或context: 表示緩存的內容
> attributes: 表示緩存文件的一些屬性;
> name: 緩存文件的path;