zoukankan      html  css  js  c++  java
  • JAVA缓存技术

    最近再ITEYE上看到关于讨论JAVA缓存技术的帖子比较多,自己不懂,所以上网大概搜了下,找到一篇,暂作保存,后面如果有用到可以参考。此为转贴,帖子来处:http://cogipard.info/articles/cache-static-files-with-jnotify-and-ehcache

    介绍
    JNotify:http://jnotify.sourceforge.net/,通过JNI技术,让Java代码可以实时的监控制定文件夹内文件的变动信息,支持Linux/Windows/MacOS;
    EHCache:http://ehcache.org/,一个广泛使用的Java缓存模块,可以做使用内存和文件完成缓存工作。
    在Java Web项目中,为了提高WEB应用的响应速度,可以把常用的静态文件(包括css,js和其他各种图片)提前读入到内存缓存中,这样可以减少很多文件系统的IO操作(这往往也是项目性能的瓶颈之一)。但是这么做往往有一个弊端,那就是当实际的静态文件发生改变的时候,缓存并不能得到及时的刷新,造成了一定的滞后现象。有些项目可能没什么问题,但是对于某些项目而言,必须解决这个问题。办法基本有两种,一种是另外开启一个线程,不断的扫描文件,和缓存的文件做比较,确定该文件时候修改,另外就是使用系统的API,来监控文件的改变。前面一种解决办法缺点很明显,费时费力,后面的办法需要用到JNI,并且编写一些系统的本地库函数,幸运的是,JNoify为我们做好了准备工作,直接拿来用就可以了。

    本文会简单给出一个利用JNotify和EHCache实现静态文件缓存的一个小例子。


    JNotify的准备
    在使用JNotify之前,你需要“安装”一下JNotify。JNotify使用了JNI技术来调用系统的本地库(Win下的是dll文件,Linux下是so文件),这些库文件都已近包含在下载包中了。但是如果你直接使用JNotify的话,往往会报错:

    Java代码 复制代码 收藏代码
    1. BASH   
    2. java.lang.UnsatisfiedLinkError: no jnotify in java.library.path   
    3.     at java.lang.ClassLoader.loadLibrary(Unknown Source)   
    4.     at java.lang.Runtime.loadLibrary0(Unknown Source)   
    5.     at java.lang.System.loadLibrary(Unknown Source)   
    6.     at net.contentobjects.jnotify.win32.JNotify_win32.<clinit>(Unknown Source)   
    7.     at net.contentobjects.jnotify.win32.JNotifyAdapterWin32.<init>(Unknown Source)  
    BASH
    java.lang.UnsatisfiedLinkError: no jnotify in java.library.path
    	at java.lang.ClassLoader.loadLibrary(Unknown Source)
    	at java.lang.Runtime.loadLibrary0(Unknown Source)
    	at java.lang.System.loadLibrary(Unknown Source)
    	at net.contentobjects.jnotify.win32.JNotify_win32.<clinit>(Unknown Source)
    	at net.contentobjects.jnotify.win32.JNotifyAdapterWin32.<init>(Unknown Source)


    这是由于jnotify找不到需要的dll或者其他库文件导致的,解决办法是把jnotify压缩包里的库文件放到java.library.path所指向的文件夹中,一般在windows下可以放在[jre安装目录]/bin下即可。

    java.library.path的值可以通过System.getProperty("java.library.path")查看,但是你即使在程序中通过System.setProperty("java.library.path", "some/folder/path/contain/dll")来改变java.library.path的值,还是无法加载到对应的dll库文件,原因是JVM只在程序加载之初读取java.library.path,以后再使用java.library.path的时候,用的都是最一开始加载到得那个值。有人认为只是一个bug,并且报告给了SUN(http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4280189)但是好像SUN不认为这是一个BUG。
    除了把dll文件放到[jre安装目录]/bin下,也可以手动指定程序的启动参数:
    java -Djava.library.path=some/folder/path/contain/dll的方法来达到目的。

    EHCache的基本使用方法
    EHCache非常容易使用,首先我们要获得一个CacheManager的实例。CacheManager有两种获得方法,一种是实例模式,一种是单例模式。这里我们用后面一种:

    Java代码 复制代码 收藏代码
    1. //CacheManager manager = new CacheManager("src/ehcache.xml");实例模式   
    2. CacheManager.create();//单例模式,默认读取类路径下的ehcache.xml作为配置文件   
    3. Cache cache = CacheManager.getInstance().getCache("staticResourceCache");   
    4. //staticResourceCache在ehcache.xml中提前定义了  
    //CacheManager manager = new CacheManager("src/ehcache.xml");实例模式
    CacheManager.create();//单例模式,默认读取类路径下的ehcache.xml作为配置文件
    Cache cache = CacheManager.getInstance().getCache("staticResourceCache");
    //staticResourceCache在ehcache.xml中提前定义了


    ehcache.xml的简单例子:

    Java代码 复制代码 收藏代码
    1. ehcache.xml :   
    2. <?xml version="1.0" encoding="UTF-8"?>   
    3. <ehcache updateCheck="false" dynamicConfig="false">   
    4.     <diskStore path="java.io.tmpdir"/>   
    5.     <cache name="staticResourceCache"  
    6.         maxElementsInMemory="1000"  
    7.         timeToIdleSeconds="7200"  
    8.         timeToLiveSeconds="7200" >   
    9.     </cache>   
    10. </ehcache>  
    ehcache.xml :
    <?xml version="1.0" encoding="UTF-8"?>
    <ehcache updateCheck="false" dynamicConfig="false">
    	<diskStore path="java.io.tmpdir"/>
        <cache name="staticResourceCache"
    		maxElementsInMemory="1000"
    		timeToIdleSeconds="7200"
    		timeToLiveSeconds="7200" >
        </cache>
    </ehcache>


    然后就可以使用Cache实例来操纵缓存了,主要的方法是

    Java代码 复制代码 收藏代码
    1. Cache.get(Object key),Cache.put(new Element(Object key, Object value)),Cache.remove(Object key)。  
    Cache.get(Object key),Cache.put(new Element(Object key, Object value)),Cache.remove(Object key)。



    缓存静态文件
    首先需要扫描包含静态文件的文件夹,为了方便我们采用Jodd工具包:

    Java代码 复制代码 收藏代码
    1. import jodd.io.findfile.FilepathScanner;   
    2. ...   
    3. FilepathScanner fs = new FilepathScanner(){   
    4.     @Override  
    5.     protected void onFile(File file) {   
    6.         cacheStatic(file);//缓存文件的函数,实现见后面   
    7.     }   
    8. };   
    9. fs.includeDirs(true).recursive(true).includeFiles(true);   
    10. fs.scan(Configurations.THEMES_PATH);//扫描包含静态文件的文件夹  
    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代码 复制代码 收藏代码
    1. import java.util.zip.GZIPOutputStream;//JDK自带的GZip压缩工具   
    2. import jodd.io.FastByteArrayOutputStream;//GZip输出的是字节流   
    3. import jodd.io.StreamUtil;//JODD的工具类   
    4.     
    5. private static void cacheStatic(File file){   
    6.     if(!isStaticResource(file.getAbsolutePath()))   
    7.         return;   
    8.     String uri = toURI(file.getAbsolutePath());//生成一个文件标识   
    9.     FileInputStream in = null;   
    10.     StringBuilder builder = new StringBuilder();   
    11.     try {   
    12.         in = new FileInputStream(file);   
    13.         BufferedReader br = new BufferedReader(   
    14.                 new InputStreamReader(in, StringPool.UTF_8));   
    15.         String strLine;   
    16.         while ((strLine = br.readLine()) != null)   {   
    17.             builder.append(strLine);   
    18.             builder.append("\n");//!important   
    19.         }   
    20.     
    21.         FastByteArrayOutputStream bao = new FastByteArrayOutputStream();   
    22.         GZIPOutputStream go = new GZIPOutputStream(bao);   
    23.         go.write(builder.toString().getBytes());   
    24.         go.flush();   
    25.         go.close();   
    26.         cache.put(new Element(uri, bao.toByteArray()));//缓存文件的字节流   
    27.     } catch (FileNotFoundException e) {   
    28.         e.printStackTrace();   
    29.     } catch (UnsupportedEncodingException e) {   
    30.         e.printStackTrace();   
    31.     } catch (IOException e) {   
    32.         e.printStackTrace();   
    33.     } finally {   
    34.         StreamUtil.close(in);   
    35.     }   
    36. }  
    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代码 复制代码 收藏代码
    1. //监控Configurations.THEMES_PATH指向的文件夹   
    2. JNotify.addWatch(Configurations.THEMES_PATH,    
    3.         JNotify.FILE_CREATED  |    
    4.         JNotify.FILE_DELETED  |    
    5.         JNotify.FILE_MODIFIED |    
    6.         JNotify.FILE_RENAMED,    
    7.         true,  new JNotifyListener(){   
    8.     
    9.     @Override  
    10.     public void fileCreated(int wd,   
    11.             String rootPath, String name) {   
    12.         cacheStatic(new File(rootPath+name));//更新缓存   
    13.     }   
    14.     
    15.     @Override  
    16.     public void fileDeleted(int wd,   
    17.             String rootPath, String name) {   
    18.         cache.remove(toURI(rootPath)+name);//删除缓存条目   
    19.     }   
    20.     
    21.     @Override  
    22.     public void fileModified(int wd,   
    23.             String rootPath, String name) {   
    24.         cacheStatic(new File(rootPath+name));   
    25.     }   
    26.     
    27.     @Override  
    28.     public void fileRenamed(int wd,   
    29.             String rootPath, String oldName,   
    30.             String newName) {   
    31.         cache.remove(toURI(rootPath)+oldName);   
    32.         cacheStatic(new File(rootPath+newName));   
    33.     }   
    34. });  
    //监控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));
    	}
    });
  • 相关阅读:
    EntityFramework优缺点
    领导者与管理者的区别
    七个对我最好的职业建议(精简版)
    The best career advice I’ve received
    Difference between Stored Procedure and Function in SQL Server
    2015年上半年一次通过 信息系统项目管理师
    Difference between WCF and Web API and WCF REST and Web Service
    What’s the difference between data mining and data warehousing?
    What is the difference between a Clustered and Non Clustered Index?
    用new创建函数的过程发生了什么
  • 原文地址:https://www.cnblogs.com/lcuzhanglei/p/2528078.html
Copyright © 2011-2022 走看看