Ehcache

1 简介
Ehcache是一个快速的、轻量级的、易于使用的、进程内的缓存。它支持内存和磁盘缓存,是一个非常轻量级的缓存实现,从1.2 版本开始支持了集群,是从hibernate发展出来的缓存。
特点:
 快速.
 简单.
 多种缓存策略
 缓存数据有两级:内存和磁盘,因此无需担心容量问题
 缓存数据会在虚拟机重启的过程中写入磁盘
 可以通过RMI、可插入API 等方式进行分布式缓存
 具有缓存和缓存管理器的侦听接口
 支持多缓存管理器实例,以及一个实例的多个缓存区域
 提供Hibernate 的缓存实现

2 helloworld
1)依赖的jar包有:

[img]http://dl2.iteye.com/upload/attachment/0107/7239/d10cb91d-e4a5-3c89-815b-0ef38f4e4297.jpg[/img]

2)将ehcache.xsd放到src目录。
3)ehcache.xml放到src目录。
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="false"
monitoring="autodetect" dynamicConfig="true">
<diskStore path="d:/ehcache"/>

<defaultCache maxElementsInMemory="1000" eternal="false"
overflowToDisk="true" timeToIdleSeconds="1200" timeToLiveSeconds="1200">
</defaultCache>

<cache name="helloworldCache" maxElementsInMemory="1000"
maxEntriesLocalDisk="10000" eternal="true" overflowToDisk="true"
diskSpoolBufferSizeMB="20"
memoryStoreEvictionPolicy="LFU" />
</ehcache>

4)HelloworldMain:
public class HelloworldMain {

static CacheManager manager ;
static String cacheName ;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
manager = CacheManager.getInstance();
cacheName = "helloworldCache";
}

@AfterClass
public static void tearDownAfterClass() throws Exception {
manager.shutdown();
}

@Before
public void setUp() throws Exception {

}

@After
public void tearDown() throws Exception {

}

@Test
public void test() throws Exception{
Cache cache = manager.getCache(cacheName);
String key = "key";
String value = key.hashCode() + "";

Element element = new Element(key, value);
cache.put(element);
System.out.println(cache.get(key));
new CacheThread().start();
}
class CacheThread extends Thread{
@Override
public void run() {
Cache cache = manager.getCache(cacheName);
System.out.println("in new thread=" + cache.get("key"));
}
}

}
输出结果:
[ key = key, value=106079, version=1, hitCount=1, CreationTime = 1429424818549, LastAccessTime = 1429424818552 ]
in new thread=[ key = key, value=106079, version=1, hitCount=2, CreationTime = 1429424818549, LastAccessTime = 1429424818553 ]

可见第2个线程也获取到了缓存的数据。

3 核心类介绍
1)Ehcache整理结构图:


[img]http://dl2.iteye.com/upload/attachment/0107/7241/1cbde814-2b69-32b6-ad6f-7338e08db167.jpg[/img]

2)

[img]http://dl2.iteye.com/upload/attachment/0107/7245/46f5a67a-3f99-39e0-8552-3c1f0aeeecb5.jpg[/img]

一个CacheManager包含多个Cache,一个Cache包含多个Element
3)CacheManager:缓存管理器,可以通过多种方式实例化,new,单例方式,URL,InputStream,
Configuration等方式,也可以通过编程方式来配置,所有的配置项均有默认值。
4)Cache:存放Element数据,Element可以存储在MemoryStore或者DiskStore中。
5)Element:存储在Cache中的的最基本元素。如果需要集群复制或者存储到disk中,则要求放在Element中的对象是可序列化的。Element包含key,value,还有一些访问控制信息如hitcount,对象存活时间等。


4 配置文件常用参数解释
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="false"
monitoring="autodetect" dynamicConfig="true">
<!-- 将缓存数据写到硬盘上的目录,文件名为"缓存名1.data"的隐藏文件,程序运行结束后会自动删除,如果要查看可以通过Thread.sleep(60*1000)暂停住程序-->
<diskStore path="d:/ehcache"/>
<!--默认的缓存策略
maxElementsInMemory :内存中存放的最大对象数,这是是对象的数量,maxBytesLocalHeap是对象的大小,jdk不提供直接计算对象大小的api,ehcache实现了计算对象大小的功能。具体可参见源码cache.calculateInMemorySize(),计算方法比较复杂。
eternal:true表示永不过期,默认是false,如果设置成true,则timeToIdleSeconds和timeToLiveSeconds就失效了。
overflowToDisk:true表示如果缓存的数量超过了maxElementsInMemory则将缓存数据放入硬盘中,
timeToIdleSeconds:允许对象处于空闲状态的时间,超过此时间则过期,清掉缓存数据。
timeToLiveSeconds:允许对象最大存活时间,超过此时间则过期,清掉缓存数据。这个时间大于timeToIdleSeconds才有意义。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。


-->
<defaultCache maxElementsInMemory="1000" eternal="false"
overflowToDisk="true" timeToIdleSeconds="1200" timeToLiveSeconds="1200">
</defaultCache>

<cache name="helloworldCache" maxElementsInMemory="1000"
maxEntriesLocalDisk="10000" eternal="true" overflowToDisk="true"
diskSpoolBufferSizeMB="20"
memoryStoreEvictionPolicy="LFU" />
</ehcache>


5 Hibernate+ehcache
Hibernate的一级缓存是session级缓存,默认就有。 二级缓存默认是没有的,需要集成ehcache,oscache,jbosscache等缓存组件支持,二级缓存是进程级和集群级的缓存。
1)hibernate.properties

hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider
hibernate.cache.use_query_cache=true

2).hbm.xml文件
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//HIbernate/HibernateMapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!--<class name="org.frank1234.jpa.helloworld.Course" table="course">-->
<class name="org.frank1234.hibernate.helloworld.Course" table="course">
[b][color=red]<cache usage="read-write" region="courseCache"/>[/color][/b]
<id name="id" column="id">
<generator class="identity"/>
</id>
<property name="name" />
</class>
<query name="getCourse">
from Course where id=?1
</query>
</hibernate-mapping>


3)代码片段:
@Test
public void load() throws Exception{
Session session = HiberanteUtil.currentSession();
session.getTransaction().begin();
org.frank1234.hibernate.helloworld.Course course = (org.frank1234.hibernate.helloworld.Course)session.load(Course.class, 9);
System.out.println(course);
course.setName("ehcachenew");
session.getTransaction().commit();
HiberanteUtil.closeSession();

Session session2 = HiberanteUtil.currentSession();
org.frank1234.hibernate.helloworld.Course course2 = (org.frank1234.hibernate.helloworld.Course)session2.load(Course.class, 9);
System.out.println(course2);
HiberanteUtil.closeSession();

}

输出结果:
Hibernate: select course0_.id as id0_0_, course0_.name as name0_0_ from course course0_ where course0_.id=?
id=9,name=ehcache
Hibernate: update course set name=? where id=?
id=9,name=ehcachenew

结论:
第二次查询时未输出sql语句,也就是说第二次是从ehcache中直接获取的。
第二次输出的是更改后的值,也就是说ehcache自动保证数据更新后的缓存更新的,而不是更新后清除缓存,下次获取的时候再放入缓存。

4)查询缓存
@Test
public void list(){
Session session = HiberanteUtil.currentSession();
try{
Query query = session.createQuery("select c from org.frank1234.hibernate.helloworld.Course c");
query.setCacheable(true);
query.setCacheRegion("courseCache");

for(int i=0;i<10;i++){
List list = query.list();
// Object obj = (Object)list.get(i);
// System.out.println(list.get(i));
}

}catch(Exception e){
e.printStackTrace();
}finally {
HiberanteUtil.closeSession();
}
}


如果开启query.setCacheable(true);
query.setCacheRegion("courseCache");
则结果输出sql查询1次。如果去掉则每次都从数据库中获取。另外需要注意的是这个设置和hibernate.properties中的“hibernate.cache.use_query_cache=true”两个设置缺一不可。

6 集群
ehcache支持五种集群方案,Terracotta、RMI、JMS、JGroups、EhCache Server。
下面简单介绍一下rmi方式。


1)rmi方式结构:

[img]http://dl2.iteye.com/upload/attachment/0107/7243/873f04a9-cf79-34bb-aa24-587b4e83c459.jpg[/img]

采用 RMI 集群模式时,集群中的每个节点都是对等关系,并不存在主节点或者从节点的概念,因此节点间必须有一个机制能够互相认识对方,必须知道其它节点的信息,包括主机地址、端口号等。EhCache 提供两种节点的发现方式:手工配置和自动发现。手工配置方式要求在每个节点中配置其它所有节点的连接信息,一旦集群中的节点发生变化时,需要对缓存进行重新配置。由于RMI是Java 中内置支持的技术,因此使用 RMI 集群模式时,无需引入其它的Jar包,Ehcache 本身就带有支持 RMI 集群的功能。

2)我们通过两个进程来模拟集群。
CluserMainA:
public class ClusterMainA {

static CacheManager manager ;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
URL url = ClusterMainA.class.getClassLoader().getResource(
"ehcache_clusterA.xml");
manager = new CacheManager(url);
}

@AfterClass
public static void tearDownAfterClass() throws Exception {
manager.shutdown();
}

@Before
public void setUp() throws Exception {

}

@After
public void tearDown() throws Exception {

}

@Test
public void test() {
Cache cache1 = manager.getCache("UserCache");
String key = "key";
String value = key.hashCode() + "";

Element element = new Element(key, value);
cache1.put(element);
// System.out.println(cache1.get(key));
// new CacheThread().start();
}
}


ClusterMainB:

public class ClusterMainB {

static CacheManager manager ;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
URL url = ClusterMainB.class.getClassLoader().getResource(
"ehcache_clusterB.xml");
manager = new CacheManager(url);
}

@AfterClass
public static void tearDownAfterClass() throws Exception {
manager.shutdown();
}

@Before
public void setUp() throws Exception {

}

@After
public void tearDown() throws Exception {

}

@Test
public void test() throws Exception{
Cache cache = manager.getCache("UserCache");
String key = "key";

while(true){//未复制值时则一直等待
System.out.println("cluserB get()");
Element element = cache.get(key);
if(element != null){
System.out.println(element);
break;
}
Thread.sleep(10*1000);
}

}
}

ehcache_clusterA.xml:

<?xml version="1.0" encoding="UTF-8"?>

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="false"
monitoring="autodetect" dynamicConfig="true">
<diskStore path="user.home" />

<!-- 指定除自身之外的网络群体中其他提供同步的主机列表,用“|”分开不同的主机 -->
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=manual,rmiUrls=//127.0.0.1:60000/UserCache|//127.0.0.1:40000/UserCache" />

<!-- 配宿主主机配置监听程序,来发现其他主机发来的同步请求 -->
<cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
properties="port=40000,socketTimeoutMillis=120000" />

<!-- 默认缓存 -->
<defaultCache maxElementsInMemory="10000" eternal="false"
timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"
diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000"
diskPersistent="false" diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>

<!-- 缓存 -->
<cache name="UserCache" maxElementsInMemory="10000" eternal="false"
timeToIdleSeconds="100000" timeToLiveSeconds="100000" overflowToDisk="false">
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.RMICacheReplicatorFactory" />
<bootstrapCacheLoaderFactory
class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
properties="bootstrapAsynchronously=false" />
</cache>

</ehcache>


ehcache_clusterB.xml:
<?xml version="1.0" encoding="UTF-8"?>

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="false"
monitoring="autodetect" dynamicConfig="true">
<diskStore path="user.home" />

<!-- 指定除自身之外的网络群体中其他提供同步的主机列表,用“|”分开不同的主机 -->
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=manual,rmiUrls=//127.0.0.1:60000/UserCache|//127.0.0.1:40000/UserCache" />

<!-- 配宿主主机配置监听程序,来发现其他主机发来的同步请求 -->
<cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
properties="port=60000,socketTimeoutMillis=120000" />

<!-- 默认缓存 -->
<defaultCache maxElementsInMemory="10000" eternal="false"
timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"
diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000"
diskPersistent="false" diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>

<!-- 缓存 -->
<cache name="UserCache" maxElementsInMemory="10000" eternal="false"
timeToIdleSeconds="100000" timeToLiveSeconds="100000" overflowToDisk="false">
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.RMICacheReplicatorFactory" />
<bootstrapCacheLoaderFactory
class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
properties="bootstrapAsynchronously=false" />
</cache>

</ehcache>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值