Ehcache集群配置之jgroups
如果我们的项目使用Ehcache缓存的ehcache.xml配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<diskStorepath diskStorepath="java.io.tmpdir"/>
<defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120"
timeToLiveSeconds="120" diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120">
</defaultCache>
<cache name="resourceCache" maxElementsInMemory="10000" eternal="false"
timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"
maxElementsOnDisk="10000000" diskPersistent="false" diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</cache>
</ehcache>
那么问题来了,突然有一天,老板说该项目要进行分布式部署,我们会发现,一顿操作后,测试时各个部署节点都会使用各自的ehcache缓存文件,从A节点添加的数据,在B节点上无法通过缓存读取到。
这时我们就需要配置ehcache集群缓存了,接下来我们使用jgroups方式来配置ehcache集群,步骤如下:
1、使用jgroups方式配置集群,首先需要引入jgroups的jar包,pom依赖如下;
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-jgroupsreplication</artifactId>
<version>1.7</version>
</dependency>
2、在原ehcache.xml配置文件中添加jgroups集群配置信息;
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.jgroups.JGroupsCacheManagerPeerProviderFactory"
properties="connect=TCP(bind_addr=0.0.0.0;bind_port=4001):
TCPPING(initial_hosts=172.16.0.101[4001],172.16.0.102[4001];
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="::"/>
上面属性配置中
initial_hosts:是集群中所有节点的IP和端口,根据自身情况配置。
bind_addr:为本机部署的IP地址,根据自身情况配置。
bind_port:为本机部署的端口,根据自身情况配置。
class:指jgroups集群类工厂,代表使用jgroups来搭建的集群。该类是在第一步的jar包中,不引入jar包则会报错,找不到该类。
3、接下来配置哪些缓存数据使用该集群来缓存;
例如我们单机的ehcache配置文件中的resourceCache,要使该缓存数据缓存到上面的集群中,只需要在该cache节点下添加一个子元素配置,代码如下:
<cache name="resourceCache" maxElementsInMemory="10000" eternal="false"
timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"
maxElementsOnDisk="10000000" diskPersistent="false" diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<cacheEventListenerFactory class="net.sf.ehcache.distribution.jgroups.JGroupsCacheReplicatorFactory"
properties="replicateAsynchronously=true, replicatePuts=true,replicateUpdates=true,
replicateUpdatesViaCopy=true,replicateRemovals=true "/>
</cache>
至此,我们的ehcache集群缓存就搭建好了,回过头来我们会发现,集群和单机只是多了两个配置,大腿一拍,这太简单了。
附上参数说明:
cache节点的参数:
name:缓存名称。
maxElementsInMemory:缓存最大个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
maxElementsOnDisk:硬盘最大缓存个数。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
cacheEventListenerFactory节点的参数:
replicatePuts=true| false 新元素的添加是否会被复制到其他机器上.
replicateUpdates=true| false 元素的更新是否会被复制到其他机器上.
replicateRemovals=true| false 元素的移除是否会被更新到到其他机器上.
replicateAsynchronously=true| false 同步操作是异步还是同步的.默认是异步的.
replicateUpdatesViaCopy=true| false 元素的复制消息或者删除消息是否会被同步.默认是true.
asynchronousReplicationIntervalMillisdefault 1000ms 所有的更新操作是异步的时候,会在1秒内同步完成.
常见的一些问题:
1、[ ERROR ]:342 - failed setting ip_ttl
则需要在java的编译参数中添加 -Djava.net.preferIPv4Stack=true
2、节点之间访问不通时,请检查防火墙是否开放了所配置的端口
3、缓存文件无访问权限问题,请检查服务器目录是否有权限
4、缓存文件冲突时,检查各部署节点ehcache.xml配置文件中的
<diskStorepath diskStorepath="java.io.tmpdir"/>
保证各节点上面的路径配置不相同,通常我们这样配置:
<diskStorepath diskStorepath="java.io.tmpdir/项目节点名"/>
5、在docker容器中部署我们的Ehcache集群的时候会经常遇到缓存不同步的问题,后台也没有报错信息,一切看起很正常,但就是不同步缓存。
遇到该问题时,检查以下配置:
a、部署docker容器时,如果有三个及以上的容器实例时,容器启动时我们需要为容器指定固定IP(docker容器指定固定IP时需要使用自定义网桥,不能使用默认的docker0网桥)
创建自定义网桥
docker network create --subnet=172.16.0.0/16 mynet
使用上面自已创建的网桥并指定固定IP启动容器(后面的配置根据实际情况添加,下面的参数仅仅是使用自定义网桥和固定IP)
docker run --network=mynet --ip 172.16.0.101
说明:使用固定IP是为了Ehcache集群可以通过docker容器IP及端口能互相访问,如果不指定,下次重启docker容器时,IP会发生变化,导致Ehcache集群访问不到节点。
b、尤其特别的注意配置Ehcache.xml中集群配置节点cacheManagerPeerProviderFactory
中的
TCP(bind_addr=0.0.0.0;bind_port=4001)
上面的bind_addr如果是部署在docker容器中时,一定要是当前容器的IP地址,不能是0.0.0.0,如果只有一个网卡,bind_addr也可以不配置,但一定不能是0.0.0.0,切记。