集群环境中使用ehcache_动力节点Java学院整理
ehcache 是一个纯 java 的进程内缓存框架,具有快速、精干等特点,是 hibernate 中默认的 cacheprovider。
下图是 ehcache 在应用程序中的位置:
ehcache 的主要特性有:
1.快速;
2.简单;
3.多种缓存策略;
4.缓存数据有两级:内存和磁盘,因此无需担心容量问题;
5.缓存数据会在虚拟机重启的过程中写入磁盘;
6.可以通过 rmi、可插入 api 等方式进行分布式缓存;
7.具有缓存和缓存管理器的侦听接口;
8.支持多缓存管理器实例,以及一个实例的多个缓存区域;
9.提供 hibernate 的缓存实现;
10.等等 …
由于 ehcache 是进程中的缓存系统,一旦将应用部署在集群环境中,每一个节点维护各自的缓存数据,当某节点对缓存数据进行更新,这些更新的数据无法在其它节点*享,这不仅会降低节点运行的效率,而且会导致数据不同步的情况发生。例如某个网站采用 a、b 两个节点作为集群部署,当 a 节点的缓存更新后,而 b 节点缓存尚未更新就可能出现用户在浏览页面的时候,一会是更新后的数据,一会是尚未更新的数据,尽管我们也可以通过 session sticky 技术来将用户锁定在某个节点上,但对于一些交互性比较强或者是非 web 方式的系统来说,session sticky 显然不太适合。所以就需要用到 ehcache 的集群解决方案。
ehcache 从 1.7 版本开始,支持五种集群方案,分别是:
• terracotta
• rmi
• jms
• jgroups
• ehcache server
本文主要介绍其中的三种最为常用集群方式,分别是 rmi、jgroups 以及 ehcache server 。
rmi 集群模式:
rmi 是 java 的一种远程方法调用技术,是一种点对点的基于 java 对象的通讯方式。ehcache 从 1.2 版本开始就支持 rmi 方式的缓存集群。在集群环境中 ehcache 所有缓存对象的键和值都必须是可序列化的,也就是必须实现 java.io.serializable 接口,这点在其它集群方式下也是需要遵守的。
下图是 rmi 集群模式的结构图:
图 2. rmi 集群模式结构图:
采用 rmi 集群模式时,集群中的每个节点都是对等关系,并不存在主节点或者从节点的概念,因此节点间必须有一个机制能够互相认识对方,必须知道其它节点的信息,包括主机地址、端口号等。ehcache 提供两种节点的发现方式:手工配置和自动发现。手工配置方式要求在每个节点中配置其它所有节点的连接信息,一旦集群中的节点发生变化时,需要对缓存进行重新配置。
由于 rmi 是 java 中内置支持的技术,因此使用 rmi 集群模式时,无需引入其它的 jar 包,ehcache 本身就带有支持 rmi 集群的功能。使用 rmi 集群模式需要在 ehcache.xml 配置文件中定义cachemanagerpeerproviderfactory 节点。假设集群中有两个节点,分别对应的 rmi 绑定信息是:
那么对应的手工配置信息如下:
<cachemanagerpeerproviderfactory class="net.sf.ehcache.distribution.rmicachemanagerpeerproviderfactory" properties="hostname=localhost, port=4567, sockettimeoutmillis=2000, peerdiscovery=manual, rmiurls=//192.168.0.12:4567/oschina_cache|//192.168.0.13:4567/oschina_cache" />
其它节点配置类似,只需把 rmiurls 中的两个 ip 地址换成另外两个节点对应的 ip 地址即可。
接下来在需要进行缓存数据复制的区域(region)上配置如下即可:
<cache name="samplecache2" maxelementsinmemory="10" eternal="false" timetoidleseconds="100" timetoliveseconds="100" overflowtodisk="false"> <cacheeventlistenerfactory class="net.sf.ehcache.distribution.rmicachereplicatorfactory" properties="replicateasynchronously=true, replicateputs=true, replicateupdates=true, replicateupdatesviacopy=false, replicateremovals=true "/> </cache>
具体每个参数代表的意义请参考 ehcache 的手册,此处不再详细说明。
ehcache 的 rmi 集群模式还有另外一种节点发现方式,就是通过多播( multicast )来维护集群中的所有有效节点。这也是最为简单而且灵活的方式,与手工模式不同的是,每个节点上的配置信息都相同,大大方便了节点的部署,避免人为的错漏出现。
在上述三个节点的例子中,配置如下:
<cachemanagerpeerproviderfactory class="net.sf.ehcache.distribution.rmicachemanagerpeerproviderfactory" properties="peerdiscovery=automatic, multicastgroupaddress=230.0.0.1, multicastgroupport=4446, timetolive=32" />
其中需要指定节点发现模式 peerdiscovery 值为 automatic 自动;同时组播地址可以指定 d 类 ip 地址空间,范围从 224.0.1.0 到 238.255.255.255 中的任何一个地址
jgroups 集群模式
ehcache 从 1.5. 版本开始增加了 jgroups 的分布式集群模式。与 rmi 方式相比较, jgroups 提供了一个非常灵活的协议栈、可靠的单播和多播消息传输,主要的缺点是配置复杂以及一些协议栈对第三方包的依赖。
jgroups 也提供了基于 tcp 的单播 ( unicast ) 和基于 udp 的多播 ( multicast ) ,对应 rmi 的手工配置和自动发现。使用单播方式需要指定其它节点的主机地址和端口,下面是两个节点,并使用了单播方式的配置:
<cachemanagerpeerproviderfactory class="net.sf.ehcache.distribution.jgroups.jgroupscachemanagerpeerproviderfactory" properties="connect=tcp(start_port=7800): tcpping(initial_hosts=host1[7800],host2[7800];port_range=10;timeout=3000; num_initial_members=3;up_thread=true;down_thread=true): verify_suspect(timeout=1500;down_thread=false;up_thread=false): pbcast.nakack(down_thread=true;up_thread=true;gc_lag=100; retransmit_timeout=3000): pbcast.gms(join_timeout=5000;join_retry_timeout=2000;shun=false; print_local_addr=false;down_thread=true;up_thread=true)" propertyseparator="::" />
使用多播方式配置如下:
<cachemanagerpeerproviderfactory class="net.sf.ehcache.distribution.jgroups.jgroupscachemanagerpeerproviderfactory" properties="connect=udp(mcast_addr=231.12.21.132;mcast_port=45566;):ping: merge2:fd_sock:verify_suspect:pbcast.nakack:unicast:pbcast.stable:frag:pbcast.gms" propertyseparator="::" />
从上面的配置来看,jgroups 的配置要比 rmi 复杂得多,但也提供更多的微调参数,有助于提升缓存数据复制的性能。详细的 jgroups 配置参数的具体意义可参考 jgroups 的配置手册。
jgroups 方式对应缓存节点的配置信息如下:
<cache name="samplecache2" maxelementsinmemory="10" eternal="false" timetoidleseconds="100" timetoliveseconds="100" overflowtodisk="false"> <cacheeventlistenerfactory class="net.sf.ehcache.distribution.jgroups.jgroupscachereplicatorfactory" properties="replicateasynchronously=true, replicateputs=true, replicateupdates=true, replicateupdatesviacopy=false, replicateremovals=true" /> </cache>
使用组播方式的注意事项
使用 jgroups 需要引入 jgroups 的 jar 包以及 ehcache 对 jgroups 的封装包 ehcache-jgroupsreplication-xxx.jar 。
在一些启用了 ipv6 的电脑中,经常启动的时候报如下错误信息:
java.lang.runtimeexception: the type of the stack (ipv6) and the user supplied addresses (ipv4) don't match: /231.12.21.132.
解决的办法是增加 jvm 参数:-djava.net.preferipv4stack=true。如果是 tomcat 服务器,可在 catalina.bat 或者 catalina.sh 中增加如下环境变量即可:
set catalina_opts=-djava.net.preferipv4stack=true
经过实际测试发现,集群方式下的缓存数据都可以在 1 秒钟之内完成到其节点的复制
ehcache server
与前面介绍的两种集群方案不同的是, ehcache server 是一个独立的缓存服务器,其内部使用 ehcache 做为缓存系统,可利用前面提到的两种方式进行内部集群。对外提供编程语言无关的基于 http 的 restful 或者是 soap 的数据缓存操作接口。
下面是 ehcache server 提供的对缓存数据进行操作的方法:
options /{cache}}
获取某个缓存的可用操作的信息。
head /{cache}/{element}
获取缓存中某个元素的 http 头信息,例如:
curl --head http://localhost:8080/ehcache/rest/samplecache2/2
ehcache server 返回的信息如下:
http/1.1 200 ok
x-powered-by: servlet/2.5
server: glassfish/v3
last-modified: sun, 27 jul 2008 08:08:49 gmt
etag: "1217146129490"
content-type: text/plain; charset=iso-8859-1
content-length: 157
date: sun, 27 jul 2008 08:17:09 gmt
get /{cache}/{element}
读取缓存中某个数据的值。
put /{cache}/{element}
写缓存。
由于这些操作都是基于 http 协议的,因此你可以在任何一种编程语言中使用它,例如 perl、php 和 ruby 等等。
下图是 ehcache server 在应用中的架构:
图 3. ehcache server 应用架构图
ehcache server 同时也提供强大的安全机制、监控功能。在数据存储方面,最大的 ehcache 单实例在内存中可以缓存 20gb。最大的磁盘可以缓存 100gb。通过将节点整合在一起,这样缓存数据就可以跨越节点,以此获得更大的容量。将缓存 20gb 的 50 个节点整合在一起就是 1tb 了
总结
以上我们介绍了三种 ehcache 的集群方案,除了第三种跨编程语言的方案外,ehcache 的集群对应用程序的代码编写都是透明的,程序人员无需考虑缓存数据是如何复制到其它节点上。既保持了代码的轻量级,同时又支持庞大的数据集群。ehcache 可谓是深入人心。
2009 年年中,terracotta 宣布收购 ehcache 产品。terracotta 公司的产品 terracotta 是一个 jvm 级的开源群集框架,提供 http session 复制、分布式缓存、pojo 群集、跨越集群的 jvm 来实现分布式应用程序协调。最近 ehcache 主要的改进都集中在跟 terracotta 框架的集成上,这是一个真正意义上的企业级缓存解决方案。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
推荐阅读
-
集群环境中使用ehcache_动力节点Java学院整理
-
使用jaxp进行dom解析_动力节点Java学院整理
-
Java interrupt()方法使用注意_动力节点Java学院整理
-
web.xml中servlet, bean, filter, listenr 加载顺序_动力节点Java学院整理
-
Log4j详细使用教程_动力节点Java学院整理
-
Java中JDom解析XML_动力节点Java学院整理
-
Java中json使用方法_动力节点Java学院整理
-
Java Thread中start()和run()的区别_动力节点Java学院整理
-
Java中的UrlDecoder 和 UrlEncoder_动力节点Java学院整理
-
Java中实现线程的三种方式及对比_动力节点Java学院整理