天天看点

EHCachey应用

EHCache是一个纯java实现的,目前来讲使用最为广泛的java缓存库。它有着强大(分布式和memory、db等缓存策略)、轻巧(2.2.3版本有着<1mb的jar体积),使用简单等特色,深受大家广大开发者的喜爱。

本文将简单介绍EHCache的一些使用配置。之后可能会另开一篇用以介绍EHCache与其他缓存产品的对比。

EHcache的配置

我们一般使用ehcache.xml作为EHCache的配置文件,当然,文件名可以任意的。

简单使用EHCache

上面说了,EHCache是一个进程内的缓存类库(貌似没说。。。),那么我们可以在任何情况下使用EHCache。 EHCache有着合理的默认值,因此我们甚至可以不用任何配置文件就使用EHCache:

package org.xiaom.ehchache.action;

import java.util.Date;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;

public class TestAction {
	public static void main(String[] args) {
		//创建CacheManager,开启事务
		CacheManager cm=CacheManager.create();
		cm.getTransactionController().begin();
		//添加Cache
		cm.addCache("test_cache");
		//获取添加的Cache,添加元素
		Cache cache=cm.getCache("test_cache");
		cache.put(new Element("key", new Date()));
		//提交事务
		cm.getTransactionController().commit();
		//使用缓存
		Element element=cache.get("key");
		System.out.println(element.getObjectValue());
	}
}
           

从上面的代码可以看到,单纯使用EHCache是很简单的。但WEB开发不是像上面的这样过家家般简单,事实上,凡是用到缓存的应用大都数据量和访问量较大,因此,我们应该挖EHCache的更多的功能。

在Web应用中使用EHCache

在把这个标题展开来说之前,我们需要明确几个事情:

  • 对于那些需要频繁更改数据的表,使用的EHCache意义不大。因为如果程序更改了表中的内容,EHCache还需要将表中内容重新装入缓存,并且抛弃旧的内容,如此开销下来,缓存的意义也就不大了。
  • 那些实时性要求很敏感的数据(账户余额,实时排名等)也不适合采用缓存。当然,我们可以通过程序逻辑上保持缓存和库的同步,但是一旦到了集群环境中,这个问题就会变得很棘手。当然,这条论断主观性很强,屏幕前的你需要带着批判性的眼光去审视我的这个言论。盲目的听信他人等于迷失自己。虽然我真没什么好东西可以被听信的。

到这儿,我们把这个标题中的内容分为两部:

  1. 使用EHCache为数据提供缓存(整合Hibernate)
  2. 缓存常用页面

EHCache当做Hibernate的二级缓存

上面的例子展示EHCache为数据提供缓存的操作(能力),我们知道,web应用里面有些表是需要被频繁地读取和写入(在同一个事务内)的,为此,ORM框架Hibernate提供了在Session之外的二级缓存,而我们今天所看的EHCache恰恰可以做为Hibernate二级缓存的提供者(事实上,绝大部分Hibernate应用都使用了EHCache作为二级缓存内容提供者,而且EHCache也成为了Hibernate的默认缓存)。同样地,在Hibernate中使用和配置二级缓存也是很简单便捷的:

我在SSH环境中的SessionFactory配置

<!-- Hibernate的SessionFactory -->
<bean id="sessionFactory"
	class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
	<property name="dataSource">
		<ref bean="dataSource" />
	</property>
	<property name="hibernateProperties">
		<value>
			hibernate.dialect=org.hibernate.dialect.MySQLInnoDBDialect
			hibernate.show_sql=true
			hibernate.format_sql=false
			hibernate.query.substitutions=true 1, false 0
			<!-- 启用查询缓存 -->
			hibernate.cache.use_query_cache=true
			<!-- 启用二级缓存 -->
			hibernate.cache.use_second_level_cache=true
			<!-- 指定缓存提供者为EHCache -->
			hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider
		</value>
	</property>
	<property name="mappingLocations">
		<list>
			<value>classpath*:/org/xiaom/butler/bean/*.hbm.xml</value> 
		</list>
	</property>
</bean>
           

为了便于管理EHCache的配置,我们在classpath(可以是src文件夹)下建立一个ehcache.xml文件:

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">

	<!-- 指定一个文件目录,当EHCache把数据写到硬盘上时,将把数据写到这个文件目录下 -->
	<diskStore path="F:/test" />

	<!--
		带*的为必选:
		
		*name: 缓存名称,必须唯一
		*maxElementsInMemory:缓存在内存中的元素的最大数目
		*maxElementsOnDisk:缓存在磁盘中的元素的最大数目
		*overlowToDisk:内存中元素到达最大数目后是否将其写入到diskStore所指定的位置
		timeToIdleSeconds:元素可以空闲(未被命中)的最存在长时间(秒),0(默认值)为永久存在
		timeToLiveSeconds:元素可以在内存中存活的最长时间(秒),0(默认值)为永久存在
		*eternal:元素是否永久有效,如果ture,则忽略timeToIdleSeconds、timeToLiveSeconds
			两个设置,从而永久驻留内存
		diskPersistent:重启jvm时是否将内存中元素写入磁盘
		diskExpiryThreadIntervalSeconds:此盘缓存失效检查线程运行间隔
		diskSpoolBufferSizeMB:EHCache为每一个Cache分配一个缓冲区,设置该缓冲区的大小
		memoryStoreEvictionPolicy:淘汰元素时的算法:
			LFU:最少使用的元素被淘汰
			LRU:最近的、最少使用的元素被淘汰
			FIFO:先进先出
	 -->
	<cache name="org.xiaom.butler.user_cache" maxElementsInMemory="1000" eternal="true"
		overflowToDisk="true" diskPersistent="true" maxElementsOnDisk="100000"/>
		
	<!-- 默认缓存,如果找不到其他缓存就是使用默认缓存 -->
	<defaultCache maxElementsInMemory="10000" eternal="false"
		overflowToDisk="true" timeToIdleSeconds="0" timeToLiveSeconds="0"
		diskPersistent="false" diskExpiryThreadIntervalSeconds="120" />
</ehcache>
           

除此之外,我们仅需要在bean.hbm.xml上配置缓存类型(<cache usage="read-write"/>),并指明缓存名称即可:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.xiaom.butler.bean" default-lazy="false">
	<class name="User" table="user" >
		<cache usage="read-write" region="org.xiaom.butler.user_cache" />
		<id name="id" type="integer" > <generator class="identity"></generator></id>
		。。。。。
	</class>
</hibernate-mapping>
           

关于更多缓存类型的资料,我们可以参考Hibernate的官方文档或Google之。

有了上面的配置,我们就可以在dao层代码中使用缓存了:

@Repository
public class TestDao extends BaseDao<Hotel, Integer>{
	public User testCache(Integer id){
		User u=null;
		Session session=getSessionFactory().openSession();
		session.get(User.class,id);//基于ID的查询。
		session.createCriteria(User.class).<span style="color:#ff0000;">setCacheable</span>(true).list();//查询缓存,记得设置Cacheable哦
		return u;
	}
}
           

至此,EHCache作为Hibernate二级缓存提供者的配置已经完成,我们该如何验证呢?

  1. 开启hibernate.show_sql=true,观察使用了缓存后的sql语句打印情况。如果没有打印sql,很显然就是数据读取自缓存
  2. 开启log4j.logger.org.hibernate.cache=debug,观察日志记录(我没开启过)

使用EHCache缓存常用页面

细细讲来,缓存某个页面其实是个很大的问题,我们思考的地方其实有很多,例如一个电子商务网站的首页要做缓存,我们最起码应该满足如下两个要求:

  • 商品根据运维系统的数据要精确显示(定时更新页面)
  • 根据用户喜好针对性显示(页面拆分后缓存)
  • 。。。

所以这里我仅给出一个简单的实例(使用默认缓存来对index.jsp进行缓存),用以作为页面缓存的入门级参考:

<!-- 让EHCache的页面缓存Filter去过滤特定页面请求 -->
	<filter>
		<filter-name>page_cache</filter-name>
		<filter-class>net.sf.ehcache.constructs.web.filter.SimplePageCachingFilter</filter-class>
	</filter>
	<!-- index.jsp被缓存 -->
	<filter-mapping>
		<filter-name>page_cache</filter-name>
		<url-pattern>index.jsp</url-pattern>
	</filter-mapping>
           

有了以上的配置,接下来你可以自行验证缓存是否生效。

分布式环境中使用EHCache

EHCache是支持分布式方式工作的,支持RMI,JGroups,JMS三种工作方式。其配置这里就暂时不介绍了。 你可以下载我的这份DEMO。但是我不是很推荐这样做,主要是因为 EHCache的使用真的很简单。

任何需要积分的下载都是耍流氓

继续阅读