转载地址: http://blog.csdn.net/mad un/article/details/8569860
最近,在ITEYE上经常看到讨论JAVA缓存技术的帖子,自己不知道,所以在网上粗略搜索后发现了一个。 保存一段时间,如果以后有需要的话请作为参考。 这是为了转录,投稿地址为:http://cogi pard.info/articles/cache-static-files-with-jnotify-and-ehcache
介绍
jot ify:http://jnotify.SourceForge.net /通过JNI技术允许Java代码实时监视和创建文件夹中文件的变化信息,并针对Linux/Windows/MacOS操作系统
EHCache: http://ehcache.org/是一个广泛使用的Java缓存模块,使用内存和文件完成缓存。
在Java Web项目中,为了提高web APP应用程序的响应速度,可以将包含css、js和各种其他图像的常规静态文件导入内存缓存中,从而减少许多文件系统的I/o操作这往往也是项目绩效的瓶颈之一。 但是,常常存在以下缺点:如果实际的静态文件改变,则缓存不能适当更新,这会引起一定的延迟现象。 有些项目可能没有问题,但有些项目需要解决这个问题。 方法基本上有两种,一种是打开另一个线程,不断扫描文件,并与缓存的文件进行比较,以确定何时修改该文件。 另外,使用系统的API监视文件的变更。 前面解决方法的缺点很明显,很费事。 后面的方法需要使用JNI。 然后,为几个系统创建本地库函数。 幸运的是,JNoify为我们做好了准备,就这样拿来用就行了。
本文简要介绍了一个使用JNotify和EHCache的静态文件缓存的小示例。
准备通告
在使用JNotify之前,必须“安装”JNotify。 JNotify正在使用JNI技术调用系统的本地库。 Win下有dll文件,Linux下有so文件。 这些库文件现在包含在下载包中。 但是,如果你直接使用JNotify,往往会报告错误:
Java代码
巴斯夫
Java.lang.unsatisfiedlinkerror : nojnotifyinjava.library.path
at Java.lang.class loader.loadlibrary (unknown source ) )。
at Java.lang.runtime.loadlibrary0(unknown source ) )。
at Java.lang.system.loadlibrary (unknown source ) )。
at net.content objects.jnotify.win32.jnotify _ win32.(unknown source )
at net.content objects.jnotify.win32.jnotifyadapterwin 32.(unknown source )
这是因为jnotify找不到所需的dll或其他库文件。 解决方案是将jnotify压缩包中的库文件放在java.library.path指向的文件夹中,通常可以在windows下放在“JRE安装目录”/坏棒棒糖下。
java.library.path的值可以在system.getproperty (' Java.library.path ' )中引用,但以编程方式为system.setproperty ) ' Java.library 无法使用更改java.library.path的值还是将其加载到相应的dll库文件中,因为JVM仅在开始加载程序时读取java.library.path,以便以后使用Java 错误_id=4280189 )但是,SUN似乎不认为这是错误。
除了将dll文件放在“JRE安装目录”/坏棒棒糖下之外,还可以手动指定程序的启动参数:
采用Java-DJ ava.library.path=some/folder/path/contain/dll的方法实现目的。
EHCache的基本用法
EHCache很好用。 首先,获取CacheManager实例。 CacheManager有两种捕获方法:实例模式和单实例模式。 在这里使用后者:
Java代码
//cachemanagermanager=newcachemanager (src/ehcache.XML ); 实例模式
CacheManager.create (; //单实例模式,默
认读取类路径下的ehcache.xml作为配置文件Cache cache = CacheManager.getInstance().getCache("staticResourceCache");
//staticResourceCache在ehcache.xml中提前定义了
ehcache.xml的简单例子:
Java代码
ehcache.xml :
maxElementsInMemory="1000"
timeToIdleSeconds="7200"
timeToLiveSeconds="7200" >
然后就可以使用Cache实例来操纵缓存了,主要的方法是
Java代码
Cache.get(Object key),Cache.put(new Element(Object key, Object value)),Cache.remove(Object key)。
缓存静态文件
首先需要扫描包含静态文件的文件夹,为了方便我们采用Jodd工具包:
Java代码
import jodd.io.findfile.FilepathScanner;
...
FilepathScanner fs = new FilepathScanner(){
@Override
protected void onFile(File file) {
cacheStatic(file);//缓存文件的函数,实现见后面
}
};
fs.includeDirs(true).recursive(true).includeFiles(true);
fs.scan(Configurations.THEMES_PATH);//扫描包含静态文件的文件夹
一般来说,如果客户端浏览器接受GZip格式的文件的话,GZip压缩可以让传输的数据大幅度减少,所以考虑对某些缓存的静态文件提前进行GZip压缩。把读取到的静态文件内容缓存到Cache里,如果静态文件时可以用GZip来传输的话,需要把文件内容首先进行压缩。
Java代码
import java.util.zip.GZIPOutputStream;//JDK自带的GZip压缩工具
import jodd.io.FastByteArrayOutputStream;//GZip输出的是字节流
import jodd.io.StreamUtil;//JODD的工具类
private static void cacheStatic(File file){
if(!isStaticResource(file.getAbsolutePath()))
return;
String uri = toURI(file.getAbsolutePath());//生成一个文件标识
FileInputStream in = null;
StringBuilder builder = new StringBuilder();
try {
in = new FileInputStream(file);
BufferedReader br = new BufferedReader(
new InputStreamReader(in, StringPool.UTF_8));
String strLine;
while ((strLine = br.readLine()) != null) {
builder.append(strLine);
builder.append("n");//!important
}
FastByteArrayOutputStream bao = new FastByteArrayOutputStream();
GZIPOutputStream go = new GZIPOutputStream(bao);
go.write(builder.toString().getBytes());
go.flush();
go.close();
cache.put(new Element(uri, bao.toByteArray()));//缓存文件的字节流
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
StreamUtil.close(in);
}
}
当文件改变的时候,使用JNotify来改变缓存内容
Java代码
//监控Configurations.THEMES_PATH指向的文件夹
JNotify.addWatch(Configurations.THEMES_PATH,
JNotify.FILE_CREATED |
JNotify.FILE_DELETED |
JNotify.FILE_MODIFIED |
JNotify.FILE_RENAMED,
true, new JNotifyListener(){
@Override
public void fileCreated(int wd,
String rootPath, String name) {
cacheStatic(new File(rootPath+name));//更新缓存
}
@Override
public void fileDeleted(int wd,
String rootPath, String name) {
cache.remove(toURI(rootPath)+name);//删除缓存条目
}
@Override
public void fileModified(int wd,
String rootPath, String name) {
cacheStatic(new File(rootPath+name));
}
@Override
public void fileRenamed(int wd,
String rootPath, String oldName,
String newName) {
cache.remove(toURI(rootPath)+oldName);
cacheStatic(new File(rootPath+newName));
}
});