RMI 是 Java 的一种远程方法调用技术,是一种点对点的基于 Java 对象的通讯方式。EhCache 从 1.2 版本开始就支持 RMI 方式的缓存集群。在集群环境中 EhCache 所有缓存对象的键和值都必须是可序列化的,也就是必须实现 java.io.Serializable 接口,这点在其它集群方式下也是需要遵守的。
下图是 RMI 集群模式的结构图:
采用 RMI 集群模式时,集群中的每个节点都是对等关系,并不存在主节点或者从节点的概念,因此节点间必须有一个机制能够互相认识对方,必须知道其它节点的信息,包括主机地址、端口号等。EhCache 提供两种节点的发现方式:手工配置和自动发现。手工配置方式要求在每个节点中配置其它所有节点的连接信息,一旦集群中的节点发生变化时,需要对缓存进行重新配置。
由于 RMI 是 Java 中内置支持的技术,因此使用 RMI 集群模式时,无需引入其它的 Jar 包,EhCache 本身就带有支持 RMI 集群的功能。使用 RMI 集群模式需要在 ehcache.xml 配置文件中定义 cacheManagerPeerProviderFactory 节点。
分布式同步缓存要让这边的cache知道对方的cache,叫做Peer Discovery(成员发现) EHCache实现成员发现的方式有两种:
1、手动查找
A、 在ehcache.xml中配置PeerDiscovery成员发现对象
Server1配置,配置本地hostName、port是400001,分别监听192.168.8.32:400002的mobileCache和192.168.5.231:400003 的mobileCache。注意这里的mobileCache是缓存的名称,分别对应着server2、server3的cache的配置。
<?xml version="1.0" encoding="gbk"?><ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd"><diskStore path="java.io.tmpdir"/><!--集群多台服务器中的缓存,这里是要同步一些服务器的缓存server1 hostName:192.168.8.9 port:400001 cacheName:mobileCacheserver2 hostName:192.168.8.32 port:400002 cacheName:mobileCacheserver3 hostName:192.168.8.231 port:400003 cacheName:mobileCache注意:每台要同步缓存的服务器的RMI通信socket端口都不一样,在配置的时候注意设置--><!-- server1 的cacheManagerPeerProviderFactory配置 --><cacheManagerPeerProviderFactoryclass="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"properties="hostName=localhost,port=400001,socketTimeoutMillis=2000,peerDiscovery=manual,rmiUrls=//192.168.8.32:400002/mobileCache|//192.168.5.231:400003/mobileCache"/></ehcache>以上注意cacheManagerPeerProviderFactory元素出现的位置在diskStore下
同样在你的另外2台服务器上增加配置
Server2,配置本地host,port为400002,分别同步192.168.8.9:400001的mobileCache和192.168.5.231:400003的mobileCache
<!-- server2 的cacheManagerPeerProviderFactory配置 --><cacheManagerPeerProviderFactoryclass="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"properties="hostName=localhost,port=400002,socketTimeoutMillis=2000,peerDiscovery=manual,rmiUrls=//192.168.8.9:400001/mobileCache|//192.168.5.231:400003/mobileCache"/>Server3,配置本地host,port为400003,分别同步192.168.8.9:400001的mobileCache缓存和192.168.8.32:400002的mobileCache缓存
<!-- server3 的cacheManagerPeerProviderFactory配置 --><cacheManagerPeerProviderFactoryclass="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"properties="hostName=localhost,port=400003,socketTimeoutMillis=2000,peerDiscovery=manual,rmiUrls=//192.168.8.9:400001/mobileCache|//192.168.8.32:400002/mobileCache"/>这样就在三台不同的服务器上配置了手动查找cache的PeerProvider成员发现的配置了。 值得注意的是你在配置rmiUrls的时候要特别注意url不能重复出现,并且端口、地址都是对的。
如果指定,hostname将使用InetAddress.getLocalHost().getHostAddress()来得到。
警告:不要将localhost配置为本地地址127.0.0.1,因为它在网络中不可见将会导致不能从远程服务器接收信息从而不能复制。在同一台机器上有多个CacheManager的时候,你应该只用localhost来配置。
B、 下面配置缓存和缓存同步监听,需要在每台服务器中的ehcache.xml文件中增加cache配置和cacheEventListenerFactory、cacheLoaderFactory的配置
<defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="30" timeToLiveSeconds="30" overflowToDisk="false"/><!--配置自定义缓存maxElementsInMemory:缓存中允许创建的最大对象数eternal:缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期。timeToIdleSeconds:缓存数据空闲的最大时间,也就是说如果有一个缓存有多久没有被访问就会被销毁,如果该值是 0 就意味着元素可以停顿无穷长的时间。timeToLiveSeconds:缓存数据存活的时间,缓存对象最大的的存活时间,超过这个时间就会被销毁,这只能在元素不是永久驻留时有效,如果该值是0就意味着元素可以停顿无穷长的时间。overflowToDisk:内存不足时,是否启用磁盘缓存。memoryStoreEvictionPolicy:缓存满了之后的淘汰算法。每一个小时更新一次缓存(1小时过期)--><cache name="mobileCache"maxElementsInMemory="10000"eternal="false"overflowToDisk="true"timeToIdleSeconds="1800"timeToLiveSeconds="3600"memoryStoreEvictionPolicy="LFU"><!--RMI缓存分布同步查找 class使用net.sf.ehcache.distribution.RMICacheReplicatorFactory这个工厂支持以下属性:replicatePuts=true | false – 当一个新元素增加到缓存中的时候是否要复制到其他的peers。默认是true。replicateUpdates=true | false – 当一个已经在缓存中存在的元素被覆盖时是否要进行复制。默认是true。replicateRemovals= true | false – 当元素移除的时候是否进行复制。默认是true。replicateAsynchronously=true | false – 复制方式是异步的指定为true时,还是同步的,指定为false时。默认是true。replicatePutsViaCopy=true | false – 当一个新增元素被拷贝到其他的cache中时是否进行复制指定为true时为复制,默认是true。replicateUpdatesViaCopy=true | false – 当一个元素被拷贝到其他的cache中时是否进行复制指定为true时为复制,默认是true。asynchronousReplicationIntervalMillis=1000--><!-- 监听RMI同步缓存对象配置 注册相应的的缓存监听类,用于处理缓存事件,如put,remove,update,和expire --><cacheEventListenerFactoryclass="net.sf.ehcache.distribution.RMICacheReplicatorFactory"properties="replicateAsynchronously=true,replicatePuts=true,replicateUpdates=true,replicateUpdatesViaCopy=false,replicateRemovals=true "/><!-- 用于在初始化缓存,以及自动设置 --><bootstrapCacheLoaderFactory class="net.sf.ehcache.bootstrap.BootstrapCacheLoaderFactory"/></cache>
C、 这样就完成了3台服务器的配置,下面给出server1的完整的ehcache.xml的配置
<?xml version="1.0" encoding="gbk"?><ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd"><diskStore path="java.io.tmpdir"/><!--集群多台服务器中的缓存,这里是要同步一些服务器的缓存server1 hostName:192.168.8.9 port:400001 cacheName:mobileCacheserver2 hostName:192.168.8.32 port:400002 cacheName:mobileCacheserver3 hostName:192.168.8.231 port:400003 cacheName:mobileCache注意每台要同步缓存的服务器的RMI通信socket端口都不一样,在配置的时候注意设置--><!-- server1 的cacheManagerPeerProviderFactory配置 --><cacheManagerPeerProviderFactoryclass="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"properties="hostName=localhost,port=400001,socketTimeoutMillis=2000,peerDiscovery=manual,rmiUrls=//192.168.8.32:400002/mobileCache|//192.168.5.231:400003/mobileCache"/><defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="30" timeToLiveSeconds="30" overflowToDisk="false"/><!--配置自定义缓存maxElementsInMemory:缓存中允许创建的最大对象数eternal:缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期。timeToIdleSeconds:缓存数据空闲的最大时间,也就是说如果有一个缓存有多久没有被访问就会被销毁,如果该值是 0 就意味着元素可以停顿无穷长的时间。timeToLiveSeconds:缓存数据存活的时间,缓存对象最大的的存活时间,超过这个时间就会被销毁,这只能在元素不是永久驻留时有效,如果该值是0就意味着元素可以停顿无穷长的时间。overflowToDisk:内存不足时,是否启用磁盘缓存。memoryStoreEvictionPolicy:缓存满了之后的淘汰算法。每一个小时更新一次缓存(1小时过期)--><cache name="mobileCache"maxElementsInMemory="10000"eternal="false"overflowToDisk="true"timeToIdleSeconds="1800"timeToLiveSeconds="3600"memoryStoreEvictionPolicy="LFU"><!--RMI缓存分布同步查找 class使用net.sf.ehcache.distribution.RMICacheReplicatorFactory这个工厂支持以下属性:replicatePuts=true | false – 当一个新元素增加到缓存中的时候是否要复制到其他的peers。默认是true。replicateUpdates=true | false – 当一个已经在缓存中存在的元素被覆盖时是否要进行复制。默认是true。replicateRemovals= true | false – 当元素移除的时候是否进行复制。默认是true。replicateAsynchronously=true | false – 复制方式是异步的指定为true时,还是同步的,指定为false时。默认是true。replicatePutsViaCopy=true | false – 当一个新增元素被拷贝到其他的cache中时是否进行复制指定为true时为复制,默认是true。replicateUpdatesViaCopy=true | false – 当一个元素被拷贝到其他的cache中时是否进行复制指定为true时为复制,默认是true。asynchronousReplicationIntervalMillis=1000--><!-- 监听RMI同步缓存对象配置 注册相应的的缓存监听类,用于处理缓存事件,如put,remove,update,和expire --><cacheEventListenerFactoryclass="net.sf.ehcache.distribution.RMICacheReplicatorFactory"properties="replicateAsynchronously=true,replicatePuts=true,replicateUpdates=true,replicateUpdatesViaCopy=false,replicateRemovals=true "/><!-- 用于在初始化缓存,以及自动设置 --><bootstrapCacheLoaderFactory class="net.sf.ehcache.bootstrap.BootstrapCacheLoaderFactory"/></cache></ehcache>
2、自动发现
自动发现配置和手动查找的方式有一点不同,其他的地方都基本是一样的。同样在ehcache.xml中增加配置,配置如下
<!--搜索某个网段上的缓存timeToLive0是限制在同一个服务器1是限制在同一个子网32是限制在同一个网站64是限制在同一个region128是限制在同一个大洲255是不限制--><cacheManagerPeerProviderFactoryclass="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"properties="peerDiscovery=automatic, multicastGroupAddress=192.168.0.1,multicastGroupPort=400004, timeToLive=32"/>常见的问题
有一个Tomcat或者是JDK的bug,在tomcat启动时如果tomcat的安装路径中有空格的话,在启动时RMI监听器会失败。参见http://archives.java.sun.com/cgi-bin/wa?A2=ind0205&L=rmi-users&P=797和http://www.ontotext.com/kim/doc/sys-doc/faq-howto-bugs/known-bugs.html。
由于在Windows上安装Tomcat默认是装在“Program Files”文件夹里的,所以这个问题经常发生。
自动的peer discovery与广播息息相关。广播可能被路由阻拦,像Xen和VMWare这种虚拟化的技术也可以阻拦广播。如果这些都打开了,你可能还在要将你的网卡的相关配置打开。一个简单的办法可以告诉广播是否有效,
那就是使用ehcache remote debugger来看“心跳”是否可用。
你可以通过设置badly misnamed time to live来控制广播传播的距离。用广播IP协议时,timeToLive的值指的是数据包可以传递的域或是范围。约定如下:
0是限制在同一个服务器
1是限制在同一个子网
32是限制在同一个网站
64是限制在同一个region
128是限制在同一个大洲
255是不限制
译者按:上面这些资料翻译的不够准确,请读者自行寻找原文理解吧。
在Java实现中默认值是1,也就是在同一个子网中传播。改变timeToLive属性可以限制或是扩展传播的范围。
其它可能造成缓存同步失败的问题
ehcache 集群通过rmi组播 Multicast进行同步的,如果发现更新数据集群没有同步的话,可能原因:
1、开启防火墙问题
2、没有开启组播功能
windows下默认是开启的。
Linux下可能没有开启
route add -net 224.0.0.0 netmask 240.0.0.0 dev eth1
3、Linux服务器的主机名(hostname)在/etc/hosts文件中设置的IP地址为127.0.0.1。检测方法为:
home>hostname -i
如果返回值为127.0.0.1,那么这就是问题。
解决方法:修改/etc/hosts文件,127.0.0.1 IP不能对应主机名,它对应localhost就可以了。
修改之前的hosts文件:
127.0.0.1 OAServer1 localhost localhost.localhost
修改之后的hosts文件:
127.0.0.1 localhost localhost.localhost
其他的配置和手动查找方式的配置是一样的,这里就不再赘述了。关于ehcache的其他缓存配置方式这里将不再介绍,大家可以自己去研究。可以参考:
官方文档:http://www.ehcache.org/documentation/user-guide/cache-topologies#using-a-cache-server
ibm developerworks文档:http://www.ibm.com/developerworks/cn/java/j-lo-ehcache/index.html
有不懂的,或者更好的见解可以随时交流!每天都会看的。