ehcache之RMI集群配置
EhCache 是一个纯 Java 的进程内缓存框架,具有快速、精干等特点,是 Hibernate 中默认的 CacheProvider。
ehcache架构图:
由于 EhCache是进程中的缓存系统,一旦将应用部署在集群环境中,每一个节点维护各自的缓存数据,当某个节点对缓存数据进行更新,这些更新的数据无法在其它节点中共享,这不仅会降低节点运行的效率,而且会导致数据不同步的情况发生。例如某个网站采用 A、B 两个节点作为集群部署,当 A 节点的缓存更新后,而 B 节点缓存尚未更新就可能出现用户在浏览页面的时候,一会是更新后的数据,一会是尚未更新的数据,尽管我们也可以通过 Session Sticky 技术来将用户锁定在某个节点上,但对于一些交互性比较强或者是非 Web 方式的系统来说,Session Sticky 显然不太适合。所以就需要用到 EhCache 的集群解决方案。
EhCache 从 1.7 版本开始,支持五种集群方案,分别是:
- Terracotta
- RMI
- JMS
- JGroups
- EhCache Server
本文主要介绍RMI集群方式:
RMI 是 Java 的一种远程方法调用技术,是一种点对点的基于 Java 对象的通讯方式。EhCache 从 1.2 版本开始就支持 RMI 方式的缓存集群。在集群环境中 EhCache 所有缓存对象的键和值都必须是可序列化的,也就是必须实现 java.io.Serializable 接口,这点在其它集群方式下也是需要遵守的。
RMI集群模式图采用 RMI 集群模式时,集群中的每个节点都是对等关系,并不存在主节点或者从节点的概念,因此节点间必须有一个机制能够互相认识对方,必须知道其它节点的信息,包括主机地址、端口号等。EhCache 提供两种节点的发现方式:手工配置和自动发现。手工配置方式要求在每个节点中配置其它所有节点的连接信息,一旦集群中的节点发生变化时,需要对缓存进行重新配置。
由于 RMI 是 Java 中内置支持的技术,因此使用 RMI 集群模式时,无需引入其它的 Jar 包,EhCache 本身就带有支持 RMI 集群的功能。使用 RMI 集群模式需要在 ehcache.xml 配置文件中定义 cacheManagerPeerProviderFactory 节点。假设集群中有两个节点,分别对应的 RMI 绑定信息是:
【节点A 192.168.0.100 userCache | 节点B 192.168.0.101 userCache】
RMI支持两种缓存复制机制,一种为点对点复制,即指定集群中其他节点信息,另一种为组播复制,即单节点广播缓存数据,其他节点接受广播并更新缓存。
首先介绍点对点复制
需要在ehcache.xml中添加如下配置信息:
<cacheManagerPeerProviderFactory class=”net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory” properties=”peerDiscovery=manual, rmiUrls=//192.168.0.100:40001/userCache” /> <!– 缓存同步监听端口 –> <cacheManagerPeerListenerFactory class=”net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory” properties=”hostName=localhost,port=40001, socketTimeoutMillis=2000″ /> <defaultCache maxElementsInMemory=”10000″ eternal=”false” timeToIdleSeconds=”300″ timeToLiveSeconds=”300″ overflowToDisk=”false”> <cacheEventListenerFactory class=”net.sf.ehcache.distribution.RMICacheReplicatorFactory” properties=”replicateAsynchronously=true, replicatePuts=true, replicateUpdates=true, replicateUpdatesViaCopy=true, replicateRemovals=true” /> </defaultCache>
rmiUrls为当前节点缓存更新后需要将缓存复制到的目标节点,如果多个,则使用“|”分割,userCache为缓存块名(注意,如果是mybatis中使用,此处应该为mapper的namespace名称)。另外一个节点只需要修改rmiUrls列表即可。
replicateAsynchronously 对象同步是否异步完成,默认为true。如果比较紧急就设为false。 在一致性时间性要求不强的时候,设为异步可大大提供性能,因为它是异步立即返回的,而且可以批量提交。
replicateUpdatesViaCopy 是否将对象变更复制到所有节点,还是只是发送一个失效信息,让对方该缓存失效,当对方需要该缓存时重新计算载入。
默认为true。鉴于对象复制的消耗挺大的,又有锁的问题,而且对方也未必需要该对象,所以此属性建议设为false。如果业务上真的需要设为true时,就可考虑使用Terracotta了。
replicatePuts、replicateUpdates、replicateRemovals 增删改是否同步,默认都为true。但因为我们前面选择了失效算法,所以replicatePuts 要设为false。
从以上配置可明显看出,点对点缓存复制更新的缺点,如果有N多个集群几点,每个配置都需要修改。
另外一种复制方式:组播通知复制,配置如下:
<cacheManagerPeerProviderFactory class=”net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory” properties=”peerDiscovery=automatic, multicastGroupAddress=230.0.0.1, multicastGroupPort=4006, timeToLive=32″ /> <cacheManagerPeerListenerFactory class=”net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory” /> <defaultCache maxElementsInMemory=”10000″ eternal=”false” timeToIdleSeconds=”0″ timeToLiveSeconds=”0″ overflowToDisk=”false”> <cacheEventListenerFactory class=”net.sf.ehcache.distribution.RMICacheReplicatorFactory” properties=”replicateAsynchronously=true, replicatePuts=true, replicateUpdates=true, replicateUpdatesViaCopy=true, replicateRemovals=true” /> </defaultCache>
peerDiscovery为automatic,自动发现网络中各个节点,multicastGroupAddress为IP地址中D类地址(224.0.1.0 到 238.255.255.255 )中任意一个即可。此时即完成配置。
组播方式优势很明显,即不管多少个集群,配置都相同,不需要做任何修改,但是也有明显的缺点,数据不能跨网段传输;如果网络环境比较复杂的话,数据更容易丢失,无法实现缓存复制等。
总结:RMI集群配置支持两种缓存复制策略,即点对点复制和组播复制,点对点复制如果对于大型机群环境,配置问题难免是一体力活;组播方式可解决点对点复制配置复杂的问题,但是如果网络环境比较复杂的情况下,节点间缓存可能无法复制,所以根据个人的网络环境及集群数量,可选择一种更好的方式来配置缓存。
附:关于EhCache中timeToLiveSeconds, timeToIdleSeconds说明:http://blog.csdn.net/vtopqx/article/details/8522333
参考:http://www.ibm.com/developerworks/cn/java/j-lo-ehcache/http://ehcache.org/documentation/2.8/replication/rmi-replicated-caching