Ehcache集群配置之jgroups

  |   3,306 浏览

    如果我们的项目使用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,切记。