|
=====================================
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)) {
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;
|
|
|