RMI 是 Java 的一种远程方法调用技术,是一种点对点的基于 Java 对象的通讯方式。EhCache 从 1.2 版本开始就支持 RMI 方式的缓存集群。在集群环境中 EhCache 所有缓存对象的键和值都必须是可序列化的,也就是必须实现 java.io.Serializable 接口,这点在其它集群方式下也是需要遵守的。
采用 RMI 集群模式时,集群中的每个节点都是对等关系,并不存在主节点或者从节点的概念,因此节点间必须有一个机制能够互相认识对方,必须知道其它节点的信息,包括主机地址、端口号等。EhCache 提供两种节点的发现方式:手工配置和自动发现。手工配置方式要求在每个节点中配置其它所有节点的连接信息,一旦集群中的节点发生变化时,需要对缓存进行重新配置。
由于 RMI 是 Java 中内置支持的技术,因此使用 RMI 集群模式时,无需引入其它的 Jar 包,EhCache 本身就带有支持 RMI 集群的功能。使用 RMI 集群模式需要在 ehcache.xml 配置文件中定义 cacheManagerPeerProviderFactory 节点。假设集群中有两个节点,分别对应的 RMI 绑定信息是:
节点 1 192.168.0.11 4567 /oschina_cache
节点 2 192.168.0.12 4567 /oschina_cache
节点 3 192.168.0.13 4567 /oschina_cache
那么对应的手工配置信息如下:
<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 中的任何一个地址。
总体过程:
1.服务Provider,即配置cacheManagerPeerProviderFactory ,有两种方式1.广播的方式 2.配置静态服务列表,两种方式如上。
2.服务Listener,服务Listener用来监听集群中其他服务器Ehcache的消息,所以Listener要监听本机上端口。
server1和server2配置一样,都是监听本机上40001端口:
<cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
properties="hostName=localhost, port=40001,
socketTimeoutMillis=2000"/>
3.事件Listener 通过Provider记录集群中其他服务器的地址,通过Listener在40001端口监听RMI消息,就差配置Replicator监听本地缓存增删改查的事件并发送到集群中其他服务器了。
可以在每个<cache>配置中提供一个EventListener。可以配置Replicator是同步还是异步的,并配置Put、Update、Remove等哪些事件需要同步:
<!-- Sample cache named sampleCache2. -->
<cache name ="sampleCache2"
maxEntriesLocalHeap ="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>
replicateAsynchronously 对象同步是否异步完成,默认为true。如果比较紧急就设为false。异步可以大幅提升性能
replicateUpdatesViaCopy 是否将对象变更复制到所有节点,还是只是发送一个失效信息,让对方该缓存失效,当对方需要该缓存时重新计算载入。
默认为true。鉴于对象复制的消耗挺大的,又有锁的问题,而且对方也未必需要该对象,所以此属性建议设为false。如果业务上真的需要设为true时,就可考虑使用Terracotta了。
replicatePuts、replicateUpdates、replicateRemovals 增删改是否同步,默认都为true。但因为我们前面选择了失效算法,所以replicatePuts 要设为false。
参考:http://ehcache.org/documentation/2.8/replication/rmi-replicated-caching