[关闭]
@caos 2014-09-01T08:20:10.000000Z 字数 10742 阅读 668

缓存框架

编程


各类开源的缓存解决方案

Cache特性

无论何种解决方案,都有着Cache的基本特性。
1. 时间记录
数据进入Cache的时间。
2. timeout过期时间
Cache里面的数据多久过期
3. Eviction Policy 清除策略
Cache满了之后,根据什么策略,应该清除哪些数据。
比如,最不经常被访问的数据,最久没有访问到的数据。
4. 命中率
Cache的数据被选中的比率
5. 分级Cache
有些Cache有分级的概念。比如,几乎所有的Cache都支持Region分区的概念。可以指定某一类的数据存放在特定的Region里面。JBoss Cache可以支持更多的级别。
6. 分布式Cache
分布在不同计算机上的Cache
7. 锁,事务,数据同步
一些Cache提供了完善的锁,事务支持。

以上特性,大部分Cache都有相应的API支持。这些API很直观,也很简单。

EhCahe

Ehcache从Hibernate发展而来,逐渐涵盖了Cache界的全部功能,是目前发展势头最好的一个项目,具有快速、简单、低消耗、扩展性强、支持对象或序列化缓存,支持缓存或元素的失效,提供LRU、LFU和FIFO缓存策略,支持内存缓存和硬盘缓存和分布式缓存机制等特点。其中Cache的存储方式为内存或磁盘

基本用法

  1. CacheManager cacheManager = CacheManager.create();
  2. // 或者
  3. cacheManager = CacheManager.getInstance();
  4. // 或者
  5. cacheManager = CacheManager.create("/config/ehcache.xml");
  6. // 或者
  7. cacheManager = CacheManager.create("http://localhost:8080/test/ehcache.xml");
  8. cacheManager = CacheManager.newInstance("/config/ehcache.xml");
  9. // .......
  10. // 获取ehcache配置文件中的一个cache
  11. Cache sample = cacheManager.getCache("sample");
  12. // 获取页面缓存
  13. BlockingCache cache = new BlockingCache(cacheManager.getEhcache("SimplePageCachingFilter"));
  14. // 添加数据到缓存中
  15. Element element = new Element("key", "val");
  16. sample.put(element);
  17. // 获取缓存中的对象,注意添加到cache中对象要序列化 实现Serializable接口
  18. Element result = sample.get("key");
  19. // 删除缓存
  20. sample.remove("key");
  21. sample.removeAll();
  22. // 获取缓存管理器中的缓存配置名称
  23. for (String cacheName : cacheManager.getCacheNames()) {
  24. System.out.println(cacheName);
  25. }
  26. // 获取所有的缓存对象
  27. for (Object key : cache.getKeys()) {
  28. System.out.println(key);
  29. }
  30. // 得到缓存中的对象数
  31. cache.getSize();
  32. // 得到缓存对象占用内存的大小
  33. cache.getMemoryStoreSize();
  34. // 得到缓存读取的命中次数
  35. cache.getStatistics().getCacheHits();
  36. // 得到缓存读取的错失次数
  37. cache.getStatistics().getCacheMisses();

在Spring中使用EhCahe

页面缓存与对象缓存

1.下载Jar包

2.将Jar包引入lib中

3.引入配置文件

页面缓存

页面缓存主要用Filter过滤器对请求的url进行过滤,如果该url在缓存中出现。那么页面数据就从缓存对象中获取,并以gzip压缩后返回。其速度是没有压缩缓存时速度的3-5倍,效率相当之高!其中页面缓存的过滤器有CachingFilter,一般要扩展filter或是自定义Filter都继承该CachingFilter。CachingFilter功能可以对HTTP响应的内容进行缓存。这种方式缓存数据的粒度比较粗,例如缓存整张页面。它的优点是使用简单、效率高,缺点是不够灵活,可重用程度不高。
EHCache使用SimplePageCachingFilter类实现Filter缓存。该类继承自CachingFilter,有默认产生cache key的calculateKey()方法,该方法使用HTTP请求的URI和查询条件来组成key。也可以自己实现一个Filter,同样继承CachingFilter类,然后覆写calculateKey()方法,生成自定义的key。
CachingFilter输出的数据会根据浏览器发送的Accept-Encoding头信息进行Gzip压缩。

在使用Gzip压缩时,需注意两个问题:
1. Filter在进行Gzip压缩时,采用系统默认编码,对于使用GBK编码的中文网页来说,需要将操作系统的语言设置为:zh_CN.GBK,否则会出现乱码的问题。
2. 默认情况下CachingFilter会根据浏览器发送的请求头部所包含的Accept-Encoding参数值来判断是否进行Gzip压缩。虽然IE6/7浏览器是支持Gzip压缩的,但是在发送请求的时候却不带该参数。为了对IE6/7也能进行Gzip压缩,可以通过继承CachingFilter,实现自己的Filter,然后在具体的实现中覆写方法acceptsGzipEncoding。

  1. protected boolean acceptsGzipEncoding(HttpServletRequest request) {
  2. boolean ie6 = headerContains(request, "User-Agent", "MSIE 6.0");
  3. boolean ie7 = headerContains(request, "User-Agent", "MSIE 7.0");
  4. return acceptsEncoding(request, "gzip") || ie6 || ie7;
  5. }

对象/方法缓存

对象缓存就是将查询的数据,添加到缓存中,下次再次查询的时候直接从缓存中获取,而不去数据库中查询。
对象缓存一般是针对方法、类而来的,结合Spring的Aop对象、方法缓存就很简单。这里需要用到切面编程,用到了Spring的MethodInterceptor或是用@Aspect。

  1. package com.hoo.common.ehcache;
  2. import java.io.Serializable;
  3. import net.sf.ehcache.Cache;
  4. import net.sf.ehcache.Element;
  5. import org.aopalliance.intercept.MethodInterceptor;
  6. import org.aopalliance.intercept.MethodInvocation;
  7. import org.apache.log4j.Logger;
  8. import org.springframework.beans.factory.InitializingBean;
  9. /**
  10. * <b>function:</b> 缓存方法拦截器核心代码
  11. * @author hoojo
  12. * @createDate 2012-7-2 下午06:05:34
  13. * @file MethodCacheInterceptor.java
  14. * @package com.hoo.common.ehcache
  15. * @project Ehcache
  16. * @blog http://blog.csdn.net/IBM_hoojo
  17. * @email hoojo_@126.com
  18. * @version 1.0
  19. */
  20. public class MethodCacheInterceptor implements MethodInterceptor, InitializingBean {
  21. private static final Logger log = Logger.getLogger(MethodCacheInterceptor.class);
  22. private Cache cache;
  23. public void setCache(Cache cache) {
  24. this.cache = cache;
  25. }
  26. public void afterPropertiesSet() throws Exception {
  27. log.info(cache + " A cache is required. Use setCache(Cache) to provide one.");
  28. }
  29. public Object invoke(MethodInvocation invocation) throws Throwable {
  30. String targetName = invocation.getThis().getClass().getName();
  31. String methodName = invocation.getMethod().getName();
  32. Object[] arguments = invocation.getArguments();
  33. Object result;
  34. String cacheKey = getCacheKey(targetName, methodName, arguments);
  35. Element element = null;
  36. synchronized (this) {
  37. element = cache.get(cacheKey);
  38. if (element == null) {
  39. log.info(cacheKey + "加入到缓存: " + cache.getName());
  40. // 调用实际的方法
  41. result = invocation.proceed();
  42. element = new Element(cacheKey, (Serializable) result);
  43. cache.put(element);
  44. } else {
  45. log.info(cacheKey + "使用缓存: " + cache.getName());
  46. }
  47. }
  48. return element.getValue();
  49. }
  50. /**
  51. * <b>function:</b> 返回具体的方法全路径名称 参数
  52. * @author hoojo
  53. * @createDate 2012-7-2 下午06:12:39
  54. * @param targetName 全路径
  55. * @param methodName 方法名称
  56. * @param arguments 参数
  57. * @return 完整方法名称
  58. */
  59. private String getCacheKey(String targetName, String methodName, Object[] arguments) {
  60. StringBuffer sb = new StringBuffer();
  61. sb.append(targetName).append(".").append(methodName);
  62. if ((arguments != null) && (arguments.length != 0)) {
  63. for (int i = 0; i < arguments.length; i++) {
  64. sb.append(".").append(arguments[i]);
  65. }
  66. }
  67. return sb.toString();
  68. }
  69. }

这里的方法拦截器主要是对你要拦截的类的方法进行拦截,然后判断该方法的类路径+方法名称+参数值组合的cache key在缓存cache中是否存在。如果存在就从缓存中取出该对象,转换成我们要的返回类型。没有的话就把该方法返回的对象添加到缓存中即可。值得主意的是当前方法的参数和返回值的对象类型需要序列化。

我们需要在src目录下添加applicationContext.xml完成对MethodCacheInterceptor拦截器的配置,该配置主意是注入我们的cache对象,哪个cache来管理对象缓存,然后哪些类、方法参与该拦截器的扫描。

  1. <context:component-scan base-package="com.hoo.common.interceptor"/>
  2. <!-- 配置eh缓存管理器 -->
  3. <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
  4. <!-- 配置一个简单的缓存工厂bean对象 -->
  5. <bean id="simpleCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
  6. <property name="cacheManager" ref="cacheManager" />
  7. <!-- 使用缓存 关联ehcache.xml中的缓存配置 -->
  8. <property name="cacheName" value="mobileCache" />
  9. </bean>
  10. <!-- 配置一个缓存拦截器对象,处理具体的缓存业务 -->
  11. <bean id="methodCacheInterceptor" class="com. hoo.common.interceptor.MethodCacheInterceptor">
  12. <property name="cache" ref="simpleCache"/>
  13. </bean>
  14. <!-- 参与缓存的切入点对象 (切入点对象,确定何时何地调用拦截器) -->
  15. <bean id="methodCachePointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
  16. <!-- 配置缓存aop切面 -->
  17. <property name="advice" ref="methodCacheInterceptor" />
  18. <!-- 配置哪些方法参与缓存策略 -->
  19. <!--
  20. .表示符合任何单一字元
  21. ### +表示符合前一个字元一次或多次
  22. ### *表示符合前一个字元零次或多次
  23. ### \Escape任何Regular expression使用到的符号
  24. -->
  25. <!-- .*表示前面的前缀(包括包名) 表示print方法-->
  26. <property name="patterns">
  27. <list>
  28. <value>com.hoo.rest.*RestService*\.*get.*</value>
  29. <value>com.hoo.rest.*RestService*\.*search.*</value>
  30. </list>
  31. </property>
  32. </bean>
  33. 在ehcache.xml中添加如下cache配置
  34. <cache name="mobileCache"
  35. maxElementsInMemory="10000"
  36. eternal="false"
  37. overflowToDisk="true"
  38. timeToIdleSeconds="1800"
  39. timeToLiveSeconds="3600"
  40. memoryStoreEvictionPolicy="LFU" />

详细查看页面缓存和对象缓存

在Spring、Hibernate中使用Ehcache缓存

EhCache是Hibernate的二级缓存技术之一,可以把查询出来的数据存储在内存或者磁盘,节省下次同样查询语句再次查询数据库,大幅减轻数据库压力;

1.在hibernate.cfg.xml配置文件中添加配置,在hibernate.cfg.xml中的mapping标签上面加以下内容:

  1. <property name="hibernate.cache.provider_class">net.sf.ehcache.hibernate.SingletonEhCacheProvider</property>
  2. <!-- Enable Second-Level Cache and Query Cache Settings -->
  3. <property name="hibernate.cache.use_second_level_cache">true</property>
  4. <property name="hibernate.cache.use_query_cache">true</property>

2.如果你是整合在spring配置文件中,那么你得配置你的applicationContext.xml中相关SessionFactory的配置

  1. <prop key="hibernate.cache.use_query_cache">true</prop>
  2. <prop key="hibernate.cache.use_second_level_cache">true</prop>
  3. <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>

然后在hibernate.cfg.xml配置文件中加入使用缓存的属性

  1. <!-- class-cache config -->
  2. <class-cache class="com.hoo.hibernate.entity.User" usage="read-write" />

3.也可以在User.hbm.xml映射文件需要Cache的配置class节点下,加入类似如下格式信息:

  1. <class name="com.hoo.hibernate.entity.User" table="USER" lazy="false">
  2. <cache usage="transactional|read-write|nonstrict-read-write|read-only" />

4.使用注解的方式配置缓存

  1. @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
  2. public class User implements Serializable {
  3. }

代码示例

  1. Session s = HibernateSessionFactory.getSession();
  2. Criteria c = s.createCriteria(User.class);
  3. c.setCacheable(true);//这句必须要有
  4. System.out.println("第一次读取");
  5. List<User> users = c.list();
  6. System.out.println(users.size());
  7. HibernateSessionFactory.closeSession();
  8. s = HibernateSessionFactory.getSession();
  9. c = s.createCriteria(User.class);
  10. c.setCacheable(true);//这句必须要有
  11. System.out.println("第二次读取");
  12. users = c.list();
  13. System.out.println(users.size());
  14. HibernateSessionFactory.closeSession();

第二次查询没有打印sql语句,而是直接使用缓存中的对象。

缓存策略依据:

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注