EhCache 从 1.5. 版本开始增加了 JGroups 的分布式集群模式。与 RMI 方式相比较, JGroups 提供了一个非常灵活的协议栈、可靠的单播和多播消息传输,主要的缺点是配置复杂以及一些协议栈对第三方包的依赖,JGroups 也提供了基于 TCP 的单播 ( Unicast ) 和基于 UDP 的多播 ( Multicast ) ,对应 RMI 的手工配置和自动发现。
0、 版本依赖
ehcache-core-2.6.11.jar
ehcache-jgroupsreplication-1.7.jar
jgroups-3.0.14.final.jar
1、 配置PeerProvider
- TCP单播配置方式:
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.jgroups.JGroupsCacheManagerPeerProviderFactory"
properties="connect=TCP(bind_addr=10.118.16.134;bind_port=40134):
TCPPING(initial_hosts=10.118.16.174[40174],10.118.15.156[40156];
port_range=1;timeout=5000;num_initial_members=2):
MERGE2(min_interval=3000;max_interval=5000):
FD_ALL(interval=5000;timeout=20000):
FD(timeout=5000;max_tries=48;):
VERIFY_SUSPECT(timeout=1500):
pbcast.NAKACK(retransmit_timeout=100,200,300,600,1200,2400,4800;discard_delivered_msgs=true):
pbcast.STABLE(stability_delay=1000;desired_avg_gossip=20000;max_bytes=0):
pbcast.GMS(print_local_addr=true;join_timeout=5000)" propertySeparator="::" />
- UDP多播配置方式:
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.jgroups.JGroupsCacheManagerPeerProviderFactory"
properties="connect=UDP(mcast_addr=230.0.0.1;mcast_port=4446;bind_port=4456):
PING:MERGE2:FD_SOCK:VERIFY_SUSPECT:
pbcast.NAKACK:UNICAST:
pbcast.STABLE:FRAG:pbcast.GMS" propertySeparator="::" />
- Jgroup协议栈:
Jgroup支持多种多样的协议,它能够为集群中成员提供点对点,点对组的通信,所有通信通过通道完成。通道基于协议栈之上,协议栈中协议各自有自己特别的功能。协议栈中多种协议所具有的主要功能可以总结为:消息传输,集群发现,消息排序,消息无损耗传输,错误节点发现,集群成员管理。
传输协议:
传输协议是协议栈最底层的协议,主要包括UDP,TCP,TUNNEL。
UDP——是多播(可以配置使用多个单播)发送和接收数据,是默认推荐的传输层协议;
TCP——是单播协议,发送多播消息,JGroups使用多个TCP单播;
TUNNEL—— 使用一个外部路由进程发送消息,群组中的任何节点都要注册到这个路由,任何消息都首先发送到路由,然后路由给他的目的接收者。
发现协议:
发现协议用来发现集群中活跃的节点及集群的协调者,发现协议位于传输协议之上,因此需要传输协议的不同使用相应的发现协议。
PING——发现协议PING位于传输协议UDP或TUNNEL之上;
TCPGOSSIP——只能和路由消息一起工作,位于TCP和UDP传输协议之上;
TCPPING——明确的列出发现的成员地址,通过Ping这些已知的成员完成发现。TCPPING协议基于TCP传输协议之上;
MPING——使用IP多播发现初始的成员关系,不像其他发现协议需要借助于传输层协议发送发现消息,MPING通过它自己的套接字发送和接收多播消息。由于使用MPING协议通过多播消息完成初始发现,所以它可以使用与任何传输协议,但是它最常被TCP协议使用。TCPPING 协议需要通过initial_hosts属性明确的列出要初始发现的地址,但MPING则不需要;
错误探测协议:
错误探测协议用来发现集群中发生错误的节点。
FD——基于心跳消息,该协议需要任意一个节点周期性的Ping它的邻居节点,如果邻居节点没有返回,发生心跳消息的节点发送SUSPECT消息给集群,集群协调者接收到SUSPECT消息后验证怀疑的节点是否死掉(VERIFY_SUSPECT),如果节点被确认为死掉,协调者更新集群成员关系视图;
FD_SOCK——基于群组成员创建的TCP套接字环,集群中的任何一个节点都连接到它的邻居,集群中第一个节点连接到第二个节点,第二个节点连接到第三个节点,这样最后一个节点连接到第一个节点。这样如果某一个节点发送异常,它的邻居节点会发现异常,检测到错误;
VERIFY_SUSPECT——确认被怀疑的节点是否确实死掉了(通过发送确认消息),由集群的协调者执行;
可靠传输协议:
可靠传输协议确保群组消息确实可靠传递,并以正确的顺序(先入先出,FIFO)传递到目的地。可靠的消息传递基础是正面或非正面的确认消息(ACK或NAK)。
UNICAST——在发送单播消息时使用,UNICAST协议使用ACK模式,如果底层传输协议使用TCP,UNICAST则不需要配置,因为TCP传输本身就可以保证消息传输时能够FIFO;
pbcast.NAKACK—— 在发送多播消息时使用,这种协议下,每个消息绑定一个序列号,消息接收者根据序列号确保消息按正确的顺序传递。如果接收者发现了一个序列号的缺失,接收者安排一个周期性的任务去要求发送者重新发送该序列号的消息,当缺失的序列号的消息收到,则任务取消;
集群成员管理协议:
pbcast.GMS——群组成员关系协议,它维护者一个活着节点的列表。GMS负责群组成员加入和离开群组的请求,同时它也处理错误探测协议发送的SUSPECT协议;
流量控制协议:
FC——流量控制协议用来在集群中调节消息发送的比率和消息接收的比率。如果消息发送者的节点发送的速度很快超过了消息接收者节点处理消息的速度,这可能会导致接收到陷入异常或某些消息丢失,流量控制协议负责调解此类状况;
分裂协议:
FRAG2——当一个消息的大小大于某一确定的值时,分裂协议将消息分裂成多个小消息,然后进行发送;而在接收端,同样分裂协议将分裂的消息进行重组。不管多播还是单播发送消息,分裂协议都可以起作用;
状态交换协议:
pbcast.STATE_TRANSFER——状态交换协议负责从已经存在的节点(集群协调者)交换状态到新加入的节点;
分布式垃圾回收协议:
pbcast.STABLE——在JGroups中,所有节点必须保持已经接收到的消息以防止错误所需要的消息重传。但是如果我们永远保存接收到的消息,则我们会面临内存溢出的问题。分布式垃圾回收协议负责周期性的释放所有节点上已经被所有节点收到的消息,从而达到回收各个节点上内存的目的;
合并协议:
MERGE2——如果网络异常发生,集群可能被分割成多个区域而形成多个集群(多个协调者)。合并协议负责将分割成的多个区域重新合并成一个集群(通过多个协调者之间的沟通)。
2、 配置cache
<cache name="userCache" maxElementsInMemory="10000" eternal="false"
overflowToDisk="false" timeToIdleSeconds="1" timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU">
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.jgroups.JGroupsCacheReplicatorFactory"
properties="replicateAsynchronously=true,
replicatePuts=true,
replicateUpdates=true,
replicateUpdatesViaCopy=true,
replicateRemovals=true " />
<bootstrapCacheLoaderFactory
class="net.sf.ehcache.distribution.jgroups.JGroupsBootstrapCacheLoaderFactory"
properties="bootstrapAsynchronously=false"/>
</cache>
cacheEventListenerFactory属性properties说明:
replicateAsynchronously——对象同步是否异步完成,默认为true。如果比较紧急就设为false。 在一致性时间性要求不强的时候,设为异步可大大提供性能,因为它是异步立即返回的,而且可以批量提交。
replicateUpdatesViaCopy——是否将对象变更复制到所有节点,还是只是发送一个失效信息,让对方该缓存失效,当对方需要该缓存时重新计算载入。默认为true。鉴于对象复制的消耗挺大的,又有锁的问题,而且对方也未必需要该对象,所以此属性建议设为false。如果业务上真的需要设为true时,就可考虑使用Terracotta了。
replicatePuts——增加对象时是否同步,默认为true,如果replicateUpdatesViaCopy为false,选择了失效算法,所以replicatePuts 要设为false。
replicateUpdates——修改加对象时是否同步,默认为true。
replicateRemovals——删除加对象时是否同步,默认为true
3、 配置Spring Bean
与RMI方式相同。
<bean id="cacheManager"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="/WEB-INF/conf/ehcache.xml"/>
</bean>
<bean id="userCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager" ref="cacheManager" />
<property name="cacheName" value="userCache" />
<property name="eternal" value="true" />
<property name="overflowToDisk" value="false" />
<property name="timeToLive" value="0" />
<property name="timeToIdle" value="0" />
</bean>
4、自定义监听cacheManager
继承CacheManagerEventListenerFactory,监听Manager中Cache的变化:
public class LogCacheManagerEventListenerFactory extends CacheManagerEventListenerFactory
实现CacheManagerEventListener
public class LogCacheManagerEventListener implements CacheManagerEventListener
在ehcache.xml中引入该配置
<cacheManagerEventListenerFactory class="xxx.LogCacheManagerEventListenerFactory"/>
5、自定义监听cache
继承CacheEventListenerFactory,监听某个Cache的变化:
public class LogCacheEventListenerFactory extends CacheEventListenerFactory
实现CacheEventListener
public class LogCacheEventListener implements CacheEventListener
在ehcache.xml引入该配置
<cache name="test">
<cacheEventListenerFactory class="xxx.LogCacheEventListenerFactory"/>
</cache>