对Guava Cache的封装和使用(包括一个管理页面实现了查看统计信息、情况、查看记录等)

时间:2021-10-14 20:46:48



非常非常感谢http://blog.csdn.net/clementad/article/details/46491701


由于项目的实际情况,需要缓存一些比较不经常改动的数据在本地服务器中,以提高接口处理的速度。决定采用Guava Cache之后,整理了一些具体需求:

  1. 由于要缓存的key-value对比较多,需要一个封装好的类被继承,子类可以简单的实现把key-value缓存到Guava Cache中;
  2. 需要定义一个接口,简单的定义一个get(K key)方法,方便使用;
  3. 需要有一个管理界面,统计缓存的命中率、记录数,以便以后做出相应的调整;
  4. 需要有一个管理界面,重设、清空缓存中的数据,或使缓存中的数据失效,以强行让服务器重新从数据库获取数据,并记录重置的时间;
  5. 需要有一个管理界面,分页查看缓存中的具体内容。

现在,该系统已经实现,并已经在正式环境中运行了一段时间,日均总命中次数超过一百万,大部分缓存的命中率在98%以上,为某些接口的请求节省了一半的时间。

Guava Cache简介:

Guava Cache提供了一种把数据(key-value对)缓存到本地(JVM)内存中的机制,适用于很少会改动的数据,比如地区信息、系统配置、字典数据,等。Guava Cache与ConcurrentMap很相似,但也不完全一样。最基本的区别是ConcurrentMap会一直保存所有添加的元素,直到显式地移除。相对地,Guava Cache为了限制内存占用,通常都设定为自动回收元素。

本文介绍了一种对Guava LoadingCache的封装使用,并提供管理页面的实现。

首先,介绍一些Guava Cache的基本概念:

Guava提供两种不同的方法来加载数据:
  • CacheLoader:在build cache的时候定义一个CacheLoader来获取数据,适用的情况:有固定的方式可以根据key来加载或计算value的值,比如从数据库中获取数据
  • Callable:在get的时候传入一个Callable对象,适用的情况:如果从缓存中获取不到数据,则另外计算一个出来,并把计算结果加入到缓存中
另外,还可以使用cache.put(key, value)方法直接向缓存中插入值,但不推荐使用,因为这样会多了一步操作。
缓存回收方式: 1、基于容量的回收(size-based eviction),有两种方式,接近最大的size或weight时回收:
  • 基于maximumSize(long):一个数据项占用一个size单位,适用于value是固定大小的情况
  • 基于maximumWeight(long):对不同的数据项计算weight,适用于value不定大小的情况,比如value为Map类型时,可以把map.size()作为weight
2、定时回收(Timed Eviction)
  • expireAfterAccess(long, TimeUnit):缓存项在给定时间内没有被读/写,则回收。
  • expireAfterWrite(long, TimeUnit):缓存项在给定时间内没有被写访问(创建或覆盖),则回收。
3、基于引用的回收(Reference-based Eviction),通过使用弱引用的键或值、或软引用的值,把缓存设置为允许垃圾回收器回收:
  • CacheBuilder.weakKeys():使用弱引用存储键。当键没有其它(强或软)引用时,缓存项可以被GC回收
  • CacheBuilder.weakValues():使用弱引用存储值。当值没有其它(强或软)引用时,缓存项可以被GC回收
  • CacheBuilder.softValues():使用软引用存储值。软引用只有在响应内存需要时,才按照全局最近最少使用的顺序回收。影响性能,不推荐使用。
4、显式清除(invalidate)
  • 个别清除:Cache.invalidate(key)
  • 批量清除:Cache.invalidateAll(keys)
  • 清除所有缓存项:Cache.invalidateAll()

什么时候发生缓存清理: 使用CacheBuilder构建的缓存不会"自动"执行清理和回收工作,也不会在某个缓存项过期后马上清理,也没有诸如此类的清理机制。它是在写操作时顺带做少量的维护工作(清理);如果写操作太少,读操作的时候也会进行少量维护工作。

基于容量的回收原则: 基本原则是LRU,但是按照每个Segment来清除的。比如: 一个maximumSize为100的Cache,concurrencyLevel=4,则如果开始清除缓存时,那些segment中size>25的会被优先清除掉只剩下25个。
移除监听器(Removal Listener): 通过CacheBuilder.removalListener(RemovalListener),可以声明一个监听器,以便缓存项被移除时做一些额外操作,RemovalListener会获取移除通知[RemovalNotification],里面包含移除原因[RemovalCause]、键和值。
注:耗性能。可以使用RemovalListeners.asynchronous(RemovalListener, Executor)定义监听器为异步操作。
统计功能: CacheBuilder.recordStats()用来开启Guava Cache的统计功能。统计打开后,Cache.stats()方法会返回CacheStats对象以提供一些统计信息。具体信息可以查看CacheStats类的定义。
asMap视图: asMap视图提供了缓存的ConcurrentMap形式,但它的get方法不会往缓存中加入数据,实质上等同于cache.getIfPresent(key)。可以用它来遍历查看缓存中的所有数据。


下面介绍对Guava Cache进行封装使用的具体方法:

使用的设计模式:策略模式(Strategy) 关于这种设计模式,具体请参考:http://zz563143188.iteye.com/blog/1847029 (第13种) 这种设计模式的关系图如下:

对Guava Cache的封装和使用(包括一个管理页面实现了查看统计信息、情况、查看记录等)

总策略:利用Guava Cache来存放我们自己的数据。 图中3+1种角色和代码的对应关系如下:
  1. Context:Service.java,(策略的使用者)
  2. Strategy:ILocalCache.java,(定义策略的接口)
  3. ConcreteStrategy:LCAreaIdToArea.java,其他ILocalCache实现类 ……,(实现策略的类)
  4. 辅助类:GuavaAbstractLoadingCache.java,(实现策略的辅助类,就是封装了Guava Cache的一个类)

各角色对应的具体功能如下:
  1. Service:cache的使用者
  2. Cache接口:定义一个get()方法,通过key获取value
  3. Cache实现类:利用辅助类,实现Cache接口的get()方法
  4. Guava Cache实现辅助类:封装了对Guava Cache的利用,包括cache的创建、从数据源获取数据、定义过时策略、等
除了上面的核心功能模块,其他辅助的功能模块如下: Cache管理类:封装了对所有实现类进行管理的一些方法,包括清空Cache中的数据、查看数据、查看统计信息、等 Cache Controller:调用Cache管理类,为管理页面提供Web HTTP接口 Cache管理页面:Web页面,用于查看Cache列表、统计信息、数据,清空缓存,等

各模块的具体代码,代码中已经包括了比较详尽的注解:

主要的依赖包:

[html] view plain copy
  1. <dependency>  
  2.     <groupId>com.google.guava</groupId>  
  3.     <artifactId>guava</artifactId>  
  4.     <version>18.0</version>  
  5. </dependency>  

Cache接口:定义一个get()方法,通过key获取value

[java] view plain copy
  1. /** 
  2.  * 本地缓存接口 
  3.  * @author XuJijun 
  4.  * 
  5.  * @param <K> Key的类型 
  6.  * @param <V> Value的类型 
  7.  */  
  8. public interface ILocalCache <K, V> {  
  9.       
  10.     /** 
  11.      * 从缓存中获取数据 
  12.      * @param key 
  13.      * @return value 
  14.      */  
  15.     public V get(K key);  
  16. }  

Guava Cache实现辅助类:封装了对Guava Cache的利用,包括cache的创建、从数据源获取数据、定义过时策略、等
[java] view plain copy
  1. package com.xjj.cache.guava;  
  2.   
  3. /** 
  4.  * 抽象Guava缓存类、缓存模板。 
  5.  * 子类需要实现fetchData(key),从数据库或其他数据源(如Redis)中获取数据。 
  6.  * 子类调用getValue(key)方法,从缓存中获取数据,并处理不同的异常,比如value为null时的InvalidCacheLoadException异常。 
  7.  *  
  8.  * @author XuJijun 
  9.  * @Date 2015-05-18 
  10.  * 
  11.  * @param <K> key 类型 
  12.  * @param <V> value 类型 
  13.  */  
  14. public abstract class GuavaAbstractLoadingCache <K, V> {  
  15.     protected final Logger logger = LoggerFactory.getLogger(this.getClass());  
  16.       
  17.     //用于初始化cache的参数及其缺省值  
  18.     private int maximumSize = 1000;                 //最大缓存条数,子类在构造方法中调用setMaximumSize(int size)来更改  
  19.     private int expireAfterWriteDuration = 60;      //数据存在时长,子类在构造方法中调用setExpireAfterWriteDuration(int duration)来更改  
  20.     private TimeUnit timeUnit = TimeUnit.MINUTES;   //时间单位(分钟)  
  21.       
  22.     private Date resetTime;     //Cache初始化或被重置的时间  
  23.     private long highestSize=0//历史最高记录数  
  24.     private Date highestTime;   //创造历史记录的时间  
  25.       
  26.     private LoadingCache<K, V> cache;  
  27.       
  28.     /** 
  29.      * 通过调用getCache().get(key)来获取数据  
  30.      * @return cache 
  31.      */  
  32.     public LoadingCache<K, V> getCache() {  
  33.         if(cache == null){  //使用双重校验锁保证只有一个cache实例  
  34.             synchronized (this) {  
  35.                 if(cache == null){  
  36.                     cache = CacheBuilder.newBuilder().maximumSize(maximumSize)      //缓存数据的最大条目,也可以使用.maximumWeight(weight)代替  
  37.                             .expireAfterWrite(expireAfterWriteDuration, timeUnit)   //数据被创建多久后被移除  
  38.                             .recordStats()                                          //启用统计  
  39.                             .build(new CacheLoader<K, V>() {  
  40.                                 @Override  
  41.                                 public V load(K key) throws Exception {  
  42.                                     return fetchData(key);  
  43.                                 }  
  44.                             });  
  45.                     this.resetTime = new Date();  
  46.                     this.highestTime = new Date();  
  47.                     logger.debug("本地缓存{}初始化成功"this.getClass().getSimpleName());  
  48.                 }  
  49.             }  
  50.         }  
  51.           
  52.         return cache;  
  53.     }  
  54.       
  55.     /** 
  56.      * 根据key从数据库或其他数据源中获取一个value,并被自动保存到缓存中。 
  57.      * @param key 
  58.      * @return value,连同key一起被加载到缓存中的。  
  59.      */  
  60.     protected abstract V fetchData(K key);  
  61.   
  62.     /** 
  63.      * 从缓存中获取数据(第一次自动调用fetchData从外部获取数据),并处理异常 
  64.      * @param key 
  65.      * @return Value 
  66.      * @throws ExecutionException  
  67.      */  
  68.     protected V getValue(K key) throws ExecutionException {  
  69.         V result = getCache().get(key);  
  70.         if(getCache().size() > highestSize){  
  71.             highestSize = getCache().size();  
  72.             highestTime = new Date();  
  73.         }  
  74.   
  75.         return result;  
  76.     }  
  77.   
  78.     public long getHighestSize() {  
  79.         return highestSize;  
  80.     }  
  81.       
  82.     public Date getHighestTime() {  
  83.         return highestTime;  
  84.     }  
  85.       
  86.     public Date getResetTime() {  
  87.         return resetTime;  
  88.     }  
  89.   
  90.     public void setResetTime(Date resetTime) {  
  91.         this.resetTime = resetTime;  
  92.     }  
  93.   
  94.     public int getMaximumSize() {  
  95.         return maximumSize;  
  96.     }  
  97.   
  98.     public int getExpireAfterWriteDuration() {  
  99.         return expireAfterWriteDuration;  
  100.     }  
  101.   
  102.     /** 
  103.      * 设置最大缓存条数 
  104.      * @param maximumSize 
  105.      */  
  106.     public void setMaximumSize(int maximumSize) {  
  107.         this.maximumSize = maximumSize;  
  108.     }  
  109.   
  110.     /** 
  111.      * 设置数据存在时长(分钟) 
  112.      * @param expireAfterWriteDuration 
  113.      */  
  114.     public void setExpireAfterWriteDuration(int expireAfterWriteDuration) {  
  115.         this.expireAfterWriteDuration = expireAfterWriteDuration;  
  116.     }  
  117. }  

Cache实现类:利用辅助类,实现Cache接口的get()方法,(以一个areaId -> Area为例子) [java] view plain copy
  1. package com.xjj.entity;  
  2.   
  3. public class Area {  
  4.     private int id;  
  5.     private int parentCode;  
  6.     private String name;  
  7.     private int code;  
  8.     private String pinyin;  
  9.     private int type;  
  10.   
  11.     public char getFirstLetter(){  
  12.         return pinyin.charAt(0);  
  13.     }  
  14.   
  15. //省略其他getter和setter  
  16. }  
  17.   
  18. package com.xjj.cache.local.impl;  
  19.   
  20. /** 
  21.  * 本地缓存:areaId -> Area 
  22.  * @author XuJijun 
  23.  * 
  24.  */  
  25. @Component  
  26. public class LCAreaIdToArea extends GuavaAbstractLoadingCache<Integer, Area> implements ILocalCache<Integer, Area> {  
  27.     //@Autowired  
  28.     //private AreasDAO areasDAO;  
  29.       
  30.     //由Spring来维持单例模式  
  31.       
  32.     private LCAreaIdToArea(){  
  33.         setMaximumSize(3000); //最大缓存条数  
  34.     }  
  35.       
  36.     @Override  
  37.     public Area get(Integer key) {  
  38.         try {  
  39.             return getValue(key);  
  40.         } catch (Exception e) {  
  41.             logger.error("无法根据areaId={}获取Area,可能是数据库中无该记录。", key ,e);  
  42.             return null;  
  43.         }  
  44.     }  
  45.   
  46.     /** 
  47.      * 从数据库中获取数据 
  48.      */  
  49.     @Override  
  50.     protected Area fetchData(Integer key) {  
  51.         logger.debug("测试:正在从数据库中获取area,area id={}", key);  
  52.         //return areasDAO.getAreaById(key);  
  53.         //测试专用,实际项目使用areaDao从数据库中获取数据  
  54.         Area a = new Area();  
  55.         a.setCode(key);  
  56.         a.setId(key);  
  57.         a.setName("地区:"+key);  
  58.         a.setParentCode(Integer.valueOf(key.toString().substring(0, key.toString().length()-3)));  
  59.         a.setPinyin("pinyin:"+key);  
  60.         a.setType(AreaType.CITY.getValue());  
  61.           
  62.         return a;  
  63.     }  
  64. }  

Service:cache的使用者
[java] view plain copy
  1. /** 
  2.  * Area相关方法,使用缓存 
  3.  * @author XuJijun 
  4.  * 
  5.  */  
  6. @Service  
  7. public class AreaService implements IAreaService {  
  8.     @Resource(name="LCAreaIdToArea")  
  9.     ILocalCache<Integer, Area> lCAreaIdToArea;  
  10.   
  11.     /** 
  12.      * 根据areaId获取Area 
  13.      * @param areaId 
  14.      * @return Area 
  15.      */   
  16.     @Override  
  17.     public Area getAreaById(int areaId) {  
  18.         return lCAreaIdToArea.get(areaId);  
  19.     }  
  20.   
  21. }  

Cache管理类:封装了对所有实现类进行管理的一些方法,包括清空Cache中的数据、查看数据、查看统计信息、等 代码中所涉及到的其他类(SpringContextUtil、PageParams、PageResult)请参考源代码:https://github.com/xujijun/MyJavaStudio [java] view plain copy
  1. package com.xjj.cache.guava;  
  2.   
  3. /** 
  4.  * Guava缓存监视和管理工具 
  5.  * @author XuJijun 
  6.  * 
  7.  */  
  8. public class GuavaCacheManager {  
  9.     //保存一个Map: cacheName -> cache Object,以便根据cacheName获取Guava cache对象  
  10.     private static Map<String, ? extends GuavaAbstractLoadingCache<Object, Object>> cacheNameToObjectMap = null;  
  11.   
  12.     /** 
  13.      * 获取所有GuavaAbstractLoadingCache子类的实例,即所有的Guava Cache对象 
  14.      * @return 
  15.      */  
  16.       
  17.     @SuppressWarnings("unchecked")  
  18.     private static Map<String, ? extends GuavaAbstractLoadingCache<Object, Object>> getCacheMap(){  
  19.         if(cacheNameToObjectMap==null){  
  20.             cacheNameToObjectMap = (Map<String, ? extends GuavaAbstractLoadingCache<Object, Object>>) SpringContextUtil.getBeanOfType(GuavaAbstractLoadingCache.class);  
  21.         }  
  22.         return cacheNameToObjectMap;  
  23.           
  24.     }  
  25.       
  26.     /** 
  27.      *  根据cacheName获取cache对象  
  28.      * @param cacheName 
  29.      * @return 
  30.      */  
  31.     private static GuavaAbstractLoadingCache<Object, Object> getCacheByName(String cacheName){  
  32.         return (GuavaAbstractLoadingCache<Object, Object>) getCacheMap().get(cacheName);  
  33.     }  
  34.       
  35.     /** 
  36.      * 获取所有缓存的名字(即缓存实现类的名称) 
  37.      * @return 
  38.      */  
  39.     public static Set<String> getCacheNames() {  
  40.         return getCacheMap().keySet();  
  41.     }  
  42.       
  43.     /** 
  44.      * 返回所有缓存的统计数据 
  45.      * @return List<Map<统计指标,统计数据>> 
  46.      */  
  47.     public static ArrayList<Map<String, Object>> getAllCacheStats() {  
  48.           
  49.         Map<String, ? extends Object> cacheMap = getCacheMap();  
  50.         List<String> cacheNameList = new ArrayList<>(cacheMap.keySet());  
  51.         Collections.sort(cacheNameList);//按照字母排序  
  52.   
  53.         //遍历所有缓存,获取统计数据  
  54.         ArrayList<Map<String, Object>> list = new ArrayList<>();  
  55.         for(String cacheName : cacheNameList){  
  56.             list.add(getCacheStatsToMap(cacheName));  
  57.         }  
  58.           
  59.         return list;  
  60.     }  
  61.       
  62.     /** 
  63.      * 返回一个缓存的统计数据 
  64.      * @param cacheName 
  65.      * @return Map<统计指标,统计数据> 
  66.      */  
  67.     private static Map<String, Object> getCacheStatsToMap(String cacheName) {  
  68.         Map<String, Object> map =  new LinkedHashMap<>();  
  69.         GuavaAbstractLoadingCache<Object, Object> cache = getCacheByName(cacheName);  
  70.         CacheStats cs = cache.getCache().stats();  
  71.         NumberFormat percent = NumberFormat.getPercentInstance(); // 建立百分比格式化用  
  72.         percent.setMaximumFractionDigits(1); // 百分比小数点后的位数  
  73.         map.put("cacheName", cacheName);  
  74.         map.put("size", cache.getCache().size());  
  75.         map.put("maximumSize", cache.getMaximumSize());  
  76.         map.put("survivalDuration", cache.getExpireAfterWriteDuration());  
  77.         map.put("hitCount", cs.hitCount());  
  78.         map.put("hitRate", percent.format(cs.hitRate()));  
  79.         map.put("missRate", percent.format(cs.missRate()));  
  80.         map.put("loadSuccessCount", cs.loadSuccessCount());  
  81.         map.put("loadExceptionCount", cs.loadExceptionCount());  
  82.         map.put("totalLoadTime", cs.totalLoadTime()/1000000);       //ms  
  83.         SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
  84.         if(cache.getResetTime()!=null){  
  85.             map.put("resetTime", df.format(cache.getResetTime()));  
  86.         }  
  87.         map.put("highestSize", cache.getHighestSize());  
  88.         if(cache.getHighestTime()!=null){  
  89.             map.put("highestTime", df.format(cache.getHighestTime()));    
  90.         }  
  91.           
  92.         return map;  
  93.     }  
  94.       
  95.     /** 
  96.      * 根据cacheName清空缓存数据 
  97.      * @param cacheName 
  98.      */  
  99.     public static void resetCache(String cacheName){  
  100.         GuavaAbstractLoadingCache<Object, Object> cache = getCacheByName(cacheName);  
  101.         cache.getCache().invalidateAll();  
  102.         cache.setResetTime(new Date());  
  103.     }  
  104.   
  105.     /** 
  106.      * 分页获得缓存中的数据  
  107.      * @param pageParams 
  108.      * @return 
  109.      */  
  110.     public static PageResult<Object> queryDataByPage(PageParams<Object> pageParams) {  
  111.         PageResult<Object> data = new PageResult<>(pageParams);  
  112.           
  113.         GuavaAbstractLoadingCache<Object, Object> cache = getCacheByName((String) pageParams.getParams().get("cacheName"));  
  114.         ConcurrentMap<Object, Object> cacheMap = cache.getCache().asMap();  
  115.         data.setTotalRecord(cacheMap.size());  
  116.         data.setTotalPage((cacheMap.size()-1)/pageParams.getPageSize()+1);  
  117.           
  118.         //遍历  
  119.         Iterator<Entry<Object, Object>> entries = cacheMap.entrySet().iterator();  
  120.         int startPos = pageParams.getStartPos()-1;  
  121.         int endPos = pageParams.getEndPos()-1;   
  122.         int i=0;  
  123.         Map<Object, Object> resultMap = new LinkedHashMap<>();  
  124.         while (entries.hasNext()) {  
  125.             Map.Entry<Object, Object> entry = entries.next();  
  126.             if(i>endPos){  
  127.                 break;  
  128.             }  
  129.               
  130.             if(i>=startPos){  
  131.                 resultMap.put(entry.getKey(), entry.getValue());  
  132.             }  
  133.               
  134.             i++;  
  135.         }  
  136.         List<Object> resultList = new ArrayList<>();  
  137.         resultList.add(resultMap);  
  138.         data.setResults(resultList);  
  139.         return data;  
  140.     }  
  141. }  

Cache Controller:调用Cache管理类,为管理页面提供Web HTTP接口
[java] view plain copy
  1. package com.xjj.web.controller;  
  2.   
  3. /** 
  4.  * 本地缓存管理接口:统计信息查询、重置数据……等 
  5.  * @author XuJijun 
  6.  * 
  7.  */  
  8. @RestController  
  9. @RequestMapping("/cache/admin")  
  10. public class CacheAdminController {  
  11.       
  12.     /** 
  13.      * 查询cache统计信息 
  14.      * @param cacheName 
  15.      * @return cache统计信息 
  16.      */  
  17.     @RequestMapping(value = "/stats", method = RequestMethod.POST)  
  18.     public JsonResult cacheStats(String cacheName) {  
  19.         JsonResult jsonResult = new JsonResult();  
  20.           
  21.         //暂时只支持获取全部  
  22.           
  23.         switch (cacheName) {  
  24.         case "*":  
  25.             jsonResult.setData(GuavaCacheManager.getAllCacheStats());  
  26.             jsonResult.setMessage("成功获取了所有的cache!");  
  27.             break;  
  28.   
  29.         default:  
  30.             break;  
  31.         }  
  32.           
  33.         return jsonResult;  
  34.     }  
  35.       
  36.     /** 
  37.      * 清空缓存数据、并返回清空后的统计信息 
  38.      * @param cacheName 
  39.      * @return 
  40.      */  
  41.     @RequestMapping(value = "/reset", method = RequestMethod.POST)  
  42.     public JsonResult cacheReset(String cacheName) {  
  43.         JsonResult jsonResult = new JsonResult();  
  44.           
  45.         GuavaCacheManager.resetCache(cacheName);  
  46.         jsonResult.setMessage("已经成功重置了" + cacheName + "!");  
  47.       
  48.         return jsonResult;  
  49.     }  
  50.       
  51.     /** 
  52.      * 返回所有的本地缓存统计信息 
  53.      * @return 
  54.      */  
  55.     @RequestMapping(value = "/stats/all", method = RequestMethod.POST)  
  56.     public JsonResult cacheStatsAll() {  
  57.         return cacheStats("*");  
  58.     }  
  59.       
  60.     /** 
  61.      * 分页查询数据详情 
  62.      * @param pageSize 
  63.      * @param pageNo 
  64.      * @param cacheName 
  65.      * @return 
  66.      */  
  67.     @RequestMapping(value = "/queryDataByPage", method = RequestMethod.POST)  
  68.     public PageResult<Object> queryDataByPage(@RequestParam Map<String, String> params){  
  69.         int pageSize = Integer.valueOf(params.get("pageSize"));  
  70.         int pageNo = Integer.valueOf(params.get("pageNo"));  
  71.         String cacheName = params.get("cacheName");  
  72.           
  73.         PageParams<Object> page = new PageParams<>();  
  74.         page.setPageSize(pageSize);  
  75.         page.setPageNo(pageNo);  
  76.         Map<String, Object> param = new HashMap<>();  
  77.         param.put("cacheName", cacheName);  
  78.         page.setParams(param);  
  79.           
  80.         return GuavaCacheManager.queryDataByPage(page);  
  81.     }  
  82. }  

Cache管理页面:Web页面,用于查看Cache列表、统计信息、数据,清空缓存,等(文件名:cache-admin.html) [html] view plain copy
  1. <!DOCTYPE html>  
  2. <html>  
  3.   <head>  
  4.     <title>Cache Admin</title>  
  5.       
  6.     <meta charset="UTF-8">  
  7.       
  8.     <script src="./resources/js/jquery-2.1.4.js" charset="UTF-8" type="text/javascript"></script>  
  9.   
  10.     <style>  
  11.         .important {color : red;}  
  12.         .attention {color : orange;}  
  13.         .perfect {color : green;}  
  14.         .highlight {color : blue;}  
  15.         table{border: 1px solid #8968CD; border-collapse: collapse;}  
  16.         th,td{border: 1px solid #8968CD; padding:6px;}  
  17.         td{color: green;}  
  18.     </style>  
  19.   
  20.   </head>  
  21.     
  22.   <body>  
  23.     
  24.     <div>  
  25.         <div id="operations">  
  26.             Cache列表:   
  27.             <input type="button" value="刷新" onClick="refreshStatsAll();">   
  28.             <input type="checkbox" id="autoRefresh" onClick="toggleAutoRefreshStats();"><label for="autoRefresh">自动刷新(3s)</label>  
  29.               
  30.         </div>  
  31.         <div><pre id="response" class="attention"></pre></div>  
  32.         <div><br><pre id="responseRawData" ></pre></div>  
  33.     </div>  
  34.     
  35.   </body>  
  36.   
  37. <script>  
  38.     var autoRefreshInterval = 3000;  
  39.     var autoRefershObject;  
  40.     var requestStatsAll = {url : "/cache/admin/stats/all", params : "*", callback: requestStatsAllCallback};  
  41.   
  42.     $(function() {  
  43.         refreshStatsAll();  
  44.     });  
  45.   
  46.     function refreshStatsAll(){  
  47.         ajaxRequest(requestStatsAll.url, requestStatsAll.params, requestStatsAll.callback);  
  48.     }  
  49.       
  50.     function sizeStatistics(obj){  
  51.         var c = "当前数据量/上限:" + obj.size + "/" + obj.maximumSize;  
  52.          c += "\n历史最高数据量:" + obj.highestSize;  
  53.          c += "\n最高数据量时间:" + obj.highestTime;  
  54.                   
  55.         return c;  
  56.     }  
  57.       
  58.     function hitStatistics(obj){  
  59.         var c = "命中数量:" + obj.hitCount;  
  60.         c += "\n命中比例:" + obj.hitRate;  
  61.         c += "\n读库比例:" + obj.missRate;  
  62.                   
  63.         return c;  
  64.     }  
  65.       
  66.     function loadStatistics(obj){  
  67.         var c = "成功加载数:" + obj.loadSuccessCount;  
  68.         c += "\n失败加载数:" + obj.loadExceptionCount;  
  69.         c += "\n总加载毫秒:" + obj.totalLoadTime;  
  70.           
  71.         return c;  
  72.     }  
  73.   
  74.     function requestStatsAllCallback(jsonResult){  
  75.         var html = "<table><tr><th>Cache名称</th> <th>数据量统计</th> <th>命中统计</th> <th>加载统计</th> <th>开始/重置时间</th> <th>操作</th> </tr>";  
  76.         $.each(jsonResult.data, function(idx, obj){  
  77.             html += "<tr><th>" + obj.cacheName + "</th>"  
  78.                 + "<td>" + sizeStatistics(obj) + "</td>"  
  79.                 + "<td>" + hitStatistics(obj) + "</td>"  
  80.                 + "<td>" + loadStatistics(obj) + "</td>"  
  81.                 + "<td>" + obj.resetTime +"\n\n失效时长:" + obj.survivalDuration + "(分钟)</td>"  
  82.                 + "<td>"  
  83.                 + "<a href='javascript:void(0)' onclick='resetCache(\""+obj.cacheName+"\");'>清空缓存</a>"  
  84.                 + "\t<a href='javascript:void(0)' onclick='queryDataByPage(\""+obj.cacheName+"\");'>显示详情</a>"  
  85.                 + "</td>"  
  86.                 + "</tr>";  
  87.         });  
  88.         html += "</table>";  
  89.         $("#response").html(html);  
  90.     }  
  91.   
  92.     function resetCache(cacheName){  
  93.         $.ajax({  
  94.             type : "POST",  
  95.             url : getRootPath()+"/cache/admin/reset",  
  96.             dataType : "json", //表示返回值类型  
  97.             data : {"cacheName":cacheName},  
  98.             success : function(jsonResult){alert(jsonResult.message);refreshStatsAll();}  
  99.         });  
  100.     }  
  101.       
  102.     //定时刷新开关  
  103.     function toggleAutoRefreshStats(){  
  104.         if($("#autoRefresh").prop("checked")==true){  
  105.             autoRefershObject = setInterval(refreshStatsAll, autoRefreshInterval);  
  106.         }else{  
  107.             clearInterval(autoRefershObject);  
  108.         }  
  109.     }  
  110.       
  111.     var pageParam = {pageNo : 1, pageSize : 10, cacheName : null};  
  112.       
  113.     function resetpageParam(){  
  114.         pageParam.pageNo = 1;  
  115.         pageParam.totalPage = 0;  
  116.     }  
  117.       
  118.     function queryDataByPage(cacheName){  
  119.         resetpageParam();  
  120.         pageParam.cacheName = cacheName;  
  121.         ajaxRequest("/cache/admin/queryDataByPage", pageParam, pageQueryCallback);  
  122.     }  
  123.       
  124.     function pageQueryCallback(jsonResult){  
  125.         pageParam.totalPage = jsonResult.totalPage;  
  126.         var html = "<label class='highlight'>Cache名称:" + pageParam.cacheName + "</label><br/><br/>";  
  127.         html += "<a href='javascript:void(0)' onclick='firstPage();'>首页 </a>\t";  
  128.         html += "<a href='javascript:void(0)' onclick='previousPage();'>上一页 </a>\t";  
  129.         html += "第<input type='number' id='pageNo' min='1' max='" + jsonResult.totalPage + "' value='" + jsonResult.pageNo + "' size='" + lengthOfNum(jsonResult.totalPage) + "' />页(共" + jsonResult.totalPage + "页)\t";  
  130.         html += "<a href='javascript:void(0)' onclick='nextPage();'>下一页 </a>\t";  
  131.         html += "<a href='javascript:void(0)' onclick='lastPage();'>末页</a>\t";  
  132.         html += "<br/><br/>";  
  133.         html += JSON.stringify(jsonResult.results[0], null, "\t");  
  134.         $("#responseRawData").html(html);  
  135.           
  136.         $("#pageNo").blur(function(){  
  137.             pn = $("#pageNo").val();  
  138.             if(pn < 1){  
  139.                 pn = 1;  
  140.                 $("#pageNo").val(pn);  
  141.             }else if(pn > pageParam.totalPage){  
  142.                 pn = pageParam.totalPage;  
  143.                 $("#pageNo").val(pn);  
  144.             }  
  145.               
  146.             pageParam.pageNo=pn;  
  147.             ajaxRequest("/cache/admin/queryDataByPage", pageParam, pageQueryCallback);  
  148.         });  
  149.           
  150.         //回车  
  151.         $("#pageNo").keyup(function(event){  
  152.             if(event.which != 13){  
  153.                 return;  
  154.             }  
  155.               
  156.             pn = $("#pageNo").val();  
  157.             if(pn < 1){  
  158.                 pn = 1;  
  159.                 $("#pageNo").val(pn);  
  160.             }else if(pn > pageParam.totalPage){  
  161.                 pn = pageParam.totalPage;  
  162.                 $("#pageNo").val(pn);  
  163.             }  
  164.               
  165.             pageParam.pageNo=pn;  
  166.             ajaxRequest("/cache/admin/queryDataByPage", pageParam, pageQueryCallback);  
  167.         });  
  168.     }  
  169.       
  170.     function firstPage(){  
  171.         pageParam.pageNo=1;  
  172.         ajaxRequest("/cache/admin/queryDataByPage", pageParam, pageQueryCallback);  
  173.     }  
  174.       
  175.     function lastPage(){  
  176.         pageParam.pageNo=pageParam.totalPage;  
  177.         ajaxRequest("/cache/admin/queryDataByPage", pageParam, pageQueryCallback);  
  178.     }  
  179.       
  180.     function nextPage(){  
  181.         if(pageParam.pageNo==pageParam.totalPage){  
  182.             alert("已经是最后一页了!");  
  183.             return;  
  184.         }  
  185.         pageParam.pageNo++;  
  186.         ajaxRequest("/cache/admin/queryDataByPage", pageParam, pageQueryCallback);  
  187.     }  
  188.       
  189.     function previousPage(){  
  190.         if(pageParam.pageNo==1){  
  191.             alert("已经是第一页了!");  
  192.             return;  
  193.         }  
  194.         pageParam.pageNo--;  
  195.         ajaxRequest("/cache/admin/queryDataByPage", pageParam, pageQueryCallback);  
  196.     }  
  197.   
  198.     //js获取项目根路径,如: http://localhost:8083/uimcardprj  
  199.     function getRootPath() {  
  200.         //获取当前网址,如: http://localhost:8083/uimcardprj/share/meun.jsp  
  201.         var curWwwPath = window.document.location.href;  
  202.         //获取主机地址之后的目录,如: uimcardprj/share/meun.jsp  
  203.         var pathName = window.document.location.pathname;  
  204.         var pos = curWwwPath.indexOf(pathName);  
  205.         //获取主机地址,如: http://localhost:8083  
  206.         var localhostPath = curWwwPath.substring(0, pos);  
  207.         //获取带"/"的项目名,如:/uimcardprj  
  208.         var projectName = pathName.substring(0, pathName.substr(1).indexOf('/') + 1);  
  209.         return (localhostPath + projectName);  
  210.     }  
  211.           
  212.     //发送ajax请求  
  213.     function ajaxRequest(url, params, successCallback, contentType, errorCallback, async) {  
  214.         var _async = async || true;  
  215.   
  216.         $.ajax({  
  217.             type : "POST",  
  218.             url : getRootPath() + url,  
  219.             async : _async,  
  220.             contentType : contentType,  
  221.             dataType : "json", //表示返回值类型  
  222.             data : params,  
  223.             success : successCallback,  
  224.             error : errorCallback  
  225.         });  
  226.     }  
  227.       
  228.     function lengthOfNum(num){  
  229.         var length = 1;  
  230.         var _num = num;  
  231.         while((_num=_num/10) >= 1){  
  232.             length++;  
  233.         }  
  234.         return length;  
  235.     }  
  236. </script>  
  237.   
  238. </html>  


其他代码,包括测试页面和测试Controller请参考源代码:https://github.com/xujijun/MyJavaStudio,有问题请留言。^_^

测试页面:

对Guava Cache的封装和使用(包括一个管理页面实现了查看统计信息、情况、查看记录等)

管理页面:

对Guava Cache的封装和使用(包括一个管理页面实现了查看统计信息、情况、查看记录等)


(原创文章,转载请注明转自Clement-Xu的博客:http://blog.csdn.net/clementad/article/details/46491701,源码地址:https://github.com/xujijun/MyJavaStudio