在写完上一篇关于Oscache CacheFilter手动刷新的文章(
http://01404421.iteye.com/blog/510649)后,又感觉有些不太完美,虽然实现了可以控制的刷新但是又不能定时刷新,随后又看了一下源码,主要是com.opensymphony.oscache.web.filter.CacheFilter和com.opensymphony.oscache.web.filter.ExpiresRefreshPolicy,经过一个多小时终于实现了可以定时刷新和手动刷新的功能。废话不多说了,先贴代码:
web.xml
<filter>
<filter-name>CacheFilter</filter-name>
<filter-class>
com.cache.MyFilterCache
</filter-class>
<init-param>
<param-name>time</param-name>
<param-value>8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CacheFilter</filter-name>
<url-pattern>/index.jsp</url-pattern>
</filter-mapping>
定时8s自动刷新缓存。
com.cache.MyFilterCache
public class MyFilterCache extends CacheFilter{
private final Log log = LogFactory.getLog(this.getClass());
@Override
public void init(FilterConfig filterConfig) {
// TODO Auto-generated method stub
super.init(filterConfig);
this.setExpiresRefreshPolicy(new MyEntryRefreshPolicy(this.getTime()));//初始化刷新策略
}
@Override
public String createCacheKey(HttpServletRequest httpRequest, ServletCacheAdministrator scAdmin, Cache cache) {
return httpRequest.getServletPath();//使用访问路径来作为缓存的key,也可以不设置
}
}
createCacheKey这个方法大家可以不用重写也行,不影响。
要注意init方法中两句代码的次序,必须先调用super.init,因为在super中是用了一个Oscache自带的com.opensymphony.oscache.web.filter.ExpiresRefreshPolicy来作为刷新策略,所以我们的要放在后面来set进去覆盖默认的。默认的刷新只是根据时间来做判断。
一下是我们自己的刷新策略:
com.cache.MyEntryRefreshPolicy
public class MyEntryRefreshPolicy extends ExpiresRefreshPolicy{
public MyEntryRefreshPolicy(int refreshPeriod) {
// TODO Auto-generated constructor stub
super(refreshPeriod);
}
@Override
public boolean needsRefresh(CacheEntry entry) {
// TODO Auto-generated method stub
if(CacheUtil.isFLUSH()){
CacheUtil.setFLUSH(false);
return true;
}
return super.needsRefresh(entry);
}
}
注意needsRefresh方法,最重要的就在这里(其实也很简单,呵呵),判断是否需要刷新,如果需要则返回true去刷新,如果不需要则再去判断时间,当然这里并不是很科学,毕竟手动刷新的几率较小,而每次都要去判断,最好应该放在按照时间判断的后面。以下是一个用来记录是否需要刷新的类:
public class CacheUtil {
private static boolean FLUSH=false;//是否需要刷新缓存
public static boolean isFLUSH() {
return FLUSH;
}
public static void setFLUSH(boolean flush) {
FLUSH = flush;
}
}
手动刷新时,只需要给这个CacheUtil.setFLUSH(true)就行了。
到这里就行了,不过在我学习Oscache源码的过程当中发现有个小问题很有趣,下面是
com.opensymphony.oscache.web.filter.ExpiresRefreshPolicy的源码:
public class ExpiresRefreshPolicy implements EntryRefreshPolicy {
private long refreshPeriod;
public ExpiresRefreshPolicy(int refreshPeriod) {
this.refreshPeriod = refreshPeriod * 1000L;
}
public boolean needsRefresh(CacheEntry entry) {
long currentTimeMillis = System.currentTimeMillis();
if ((refreshPeriod >= 0) && (currentTimeMillis >= (entry.getLastUpdate() + refreshPeriod))) {
return true;
} else if (entry.getContent() instanceof ResponseContent) {
ResponseContent responseContent = (ResponseContent) entry.getContent();
return currentTimeMillis >= responseContent.getExpires();
} else {
return false;
}
}
public long getRefreshPeriod() {
return refreshPeriod / 1000;
}
public void setRefreshPeriod(long refreshPeriod) {
this.refreshPeriod = refreshPeriod * 1000L;
}
}
代码很简单吧,我去掉了所有的注释,我们都知道web.xml中设置的time参数单位是秒,但是实际上这里处理的时候是按照毫秒来处理的,从构造方法和set方法里将时间间隔乘1000,扩大为秒,其实最终用途只是用来比较时间戳的大小,个人感觉如果在并发量很大的情况下使用Oscache,这个乘1000的操作可能也会带来很大的性能损耗,其实完全可以在web.xml中按照毫秒来写,这里拿到后直接处理。
扩展一下:
以上例子只是用于一个URL,如果有多个url需要缓存同样可以设置单独刷新。不CacheUtil里可能就是一个MAP了。不过好像根据cache的key也可以刷新指定的cache,key的生产也是可以自己控制的,所以这个方案应该同样可以实现。不过刚开始我在测试group和key的刷新时没有成功,呵呵,以后再看看吧。
欢迎与各位交流。