ehcache rmi 集群配置

本文介绍了Ehcache在RMI集群模式下的配置,包括集群中的对等关系、手动配置原理,以及核心类和配置步骤。在手动配置中,每个节点与其他节点通过RMI的P2P方式实现数据同步。同时,提到了RMI注册表中的地址问题,以及可能遇到的连接超时异常解决方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考文献:

ehcache-spring-boot: springboot starter集成ehcache

ehcache rmi集群手动发现部署 有防火墙_cachemanagerpeerlistenerfactory-优快云博客^v1^pc_404_mixedpudn&depth_1-utm_source=distribute.pc_feed_404.none-task-blog-2~default~BlogCommendFromBaidu~Rate-1-122131114-blog-null.262^v1^pc_404_mixedpud 

Ehcache原理详细解读_ehcache缓存原理_爱吃牛肉的大老虎的博客-优快云博客

1、RMI集群介绍

RMI Java 的一种远程方法调用技术,是一种点对点的基于 Java 对象的通信方式。 EhCach e 从 1.2 版本开始就支持 RMI 方式的缓存集群。 在集群环境中 EhCache 全部缓存对象的键和值都必须是可序列化的,也就是必须实现 java.io.Serializable 接口,这点在其它集群方式下也是须要遵照的。
RMI 集群模式的结构图:

采用 RMI 集群模式时,集群中的每一个节点都是对等关系,并不存在主节点或者从节点的概念,所以节点间必须有一个机制可以互相认识对方,必须知道其它节点的信息,包括主机地址、端口号等。因为 RMI 是 Java 中内置支持的技术,所以使用 RMI 集群模式时,无需引入其它的 Jar 包,EhCache 自己就带有支持 RMI 集群的功能。

EhCache 提供两种节点的发现方式:手工配置自动发现

因为自动发现的方式使用的是RMI组播方式,当网络环境复杂时,例如集群成员之间跨网段通信容易出现问题,因此再此不做过多介绍。这里对手工配置,即RMI p2p方式:( 其实就是每个节点和其他n-1个节点都建立TCP的P2P PEER)进行数据同步的方式进行详细介绍。

2、手动配置详解

2.1 手工配置缓存同步原理图

说明:

1)应用1接收到缓存添加的请求,应用1调用ehcache的缓存的put()方法,方法执行时,ehcache的缓存监听器监听到PUT事件

2)根据配置的peer通信地址,通过RMI的方式调用应用2 ehcache缓存的put方法

3)调用应用2的ehcache缓存的put方法,调用成功则返回调用结果

2.2 手动查找时,防火墙配置问题

在Ehcache配置中,listener的端口和remoteObjectPort通常指的是不同类型的通信端口,它们服务于不同的目的。下面我将解释这两个端口的区别:
  1. listener 端口( CacheManagerPeerListenerFactory :
    • 这个端口用于监听来自其他Ehcache节点的连接请求。它主要用于集群环境中,使得多个Ehcache实例可以相互通信,实现缓存数据的同步或复制。
    • 当使用RMI作为传输协议时,这个端口是通过 CacheManagerPeerListenerFactory配置的,它允许其他的Ehcache节点连接到当前节点,以进行缓存更新、查询等操作。
    • 它是双向通信的一部分,即不仅接收其他节点的数据更新,也向其他节点发送本节点的数据更新。
  2. remoteObjectPort:
    • remoteObjectPort 主要与远程对象复制有关,特别是在Ehcache 2.x版本中,当使用RMI来实现分布式缓存时。
    • 这个端口是为每个导出的远程对象(例如,一个实现了远程接口的缓存管理器)指定的,用于监听来自客户端或者其他节点的调用。
    • 在集群环境中, remoteObjectPort确保了远程对象可以在特定端口上被访问,从而实现跨节点的缓存一致性维护。
总结来说, listener 端口用于节点间通信,而 remoteObjectPort 则用于远程对象的访问。
注意:如果不指定 remoteObjectPort,那么可能会随机获取一个服务器端口使用,这个时候不利于配置防火墙策略,因为服务每次启动端口号都变了。因此在使用ehcache集群时,如果部署的服务器开启防火墙,那么 remoteObjectPort是必须指定的。
防火墙策略配置(以iptables为例):
防火墙放开对40001、40002端口的限制
vim /etc/sysconfig/iptables

-A INPUT -p tcp -m state --state NEW -m tcp --dport 40001 -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 40002 -j ACCEPT

systemctl restart iptables

2.2 核心类及介绍

RMICacheReplicatorFactory

CacheEventListener 创建工厂,负责创建CacheEventListener对象

RMICacheManagerPeerProviderFactory

CacheManagerPeerProvider  创建工厂,负责创建CacheManagerPeerProvider 对象

RMICacheManagerPeerListenerFactory

CacheManagerPeerListener 创建工厂,负责创建CacheManagerPeerListener

CacheEventListener

缓存事件监听器

CacheManagerPeerProvider

成员提供者:提供需要同步的成员地址

CacheManagerPeerListener

成员监听者:对通信端口,进行监听,处理集群成员发送的消息

 2.3 配置建造者类图

2.4 配置建造者类图解析

Builder
建造者接口
CacheConfigurationBuilder
建造缓存信息配置类对象
CacheEventListenerFactoryBuilder
RMICacheReplicatorFactory建造者,负责建造 RMICacheReplicatorFactory配置类对象
CacheManagerPeerListenerFactoryBuilder
RMICacheManagerPeerListenerFactory建造者,负责建造 RMICacheManagerPeerListenerFactory配置类对象
CacheManagerPeerProviderFactoryBuilder
RMICacheManagerPeerProviderFactory建造者,负责建造 CacheManagerPeerProvider对象配置类对象
EhCacheConfigurationBuilder
ehcache 配置类建造者

2.5 ehcache 配置代码示例

Builder

package com.icfcc.fcit.dp.cache.config.ehcache.builder;


/**
* @Description:建造者接口
* @Author: gezongyang
* @Date: Created in 2022-11-11 08:59
**/
public interface Builder<T> {
   /**
    * @Description: 建造对象
    * @Author: gezongyang
    * @Param: []
    * @Return: T 返回类型
    * @Date: 2022/11/11
    **/
    T build();
}

CacheConfigurationBuilder

package com.icfcc.fcit.dp.cache.config.ehcache.builder;


import com.icfcc.fcit.dp.cache.util.EhCacheHelper;
import com.icfcc.fcit.dp.cache.properties.EhCacheProperties;
import com.icfcc.fcit.dp.cache.properties.EhCacheProperties.Cache;
import lombok.AllArgsConstructor;
import net.sf.ehcache.config.CacheConfiguration;
import org.springframework.beans.BeanUtils;


/**
* @Description:缓存配置建造者
* @Author: gezongyang
* @Date: Created in 2022-11-11 14:28
**/
@AllArgsConstructor
public class CacheConfigurationBuilder implements  Builder<CacheConfiguration>{


    String cacheName;
    EhCacheProperties ehCacheProperties;


    @Override
    public CacheConfiguration build() {
        EhCacheHelper helper = new EhCacheHelper();
        Cache cache = helper.getCache(ehCacheProperties, cacheName);
        CacheConfiguration cacheConfiguration = new  CacheConfiguration().name(cacheName);
        BeanUtils.copyProperties(cache, cacheConfiguration);
        if (ehCacheProperties.isClusterEnabled()) {
            buildCacheEventListenerFactory(cacheConfiguration, cache);
        }
        return cacheConfiguration;
    }


    /**
     * @Description: cacheEventListenerFactory  监听缓存中element的put,  remove, update和expire事件
     * 每个缓存区域都要配置一个缓存监听器,来监听缓存的变化
     * @Author: gezongyang
     * @Param: [cacheConfiguration, cache]
     * @Return: void
     * @Date: 2022/11/11
     **/
    private void buildCacheEventListenerFactory(CacheConfiguration  cacheConfiguration, Cache cache) {
        CacheEventListenerFactoryBuilder  cacheEventListenerFactoryBuilder = new  CacheEventListenerFactoryBuilder(cache);
         cacheConfiguration.addCacheEventListenerFactory(cacheEventListenerFactoryBuilder.build());
    }
}

CacheEventListenerFactoryBuilder

package com.icfcc.fcit.dp.cache.config.ehcache.builder;
import com.icfcc.fcit.dp.cache.properties.EhCacheProperties.Cache;
import lombok.AllArgsConstructor;
import  net.sf.ehcache.config.CacheConfiguration.CacheEventListenerFactoryConfiguration;
import net.sf.ehcache.distribution.RMICacheReplicatorFactory;

/**
* @Description:缓存事件监听工厂建造者
* @Author: gezongyang
* @Date: Created in 2022-11-11 14:53
**/
@AllArgsConstructor
public class CacheEventListenerFactoryBuilder implements  Builder<CacheEventListenerFactoryConfiguration>{

    private static final String CLASS_NAME =  RMICacheReplicatorFactory.class.getName();
    Cache cache;

    @Override
    public CacheEventListenerFactoryConfiguration build() {
        CacheEventListenerFactoryConfiguration  cacheEventListenerFactoryConfiguration = new  CacheEventListenerFactoryConfiguration()
                .className(CLASS_NAME);
        String properties =  cache.getRmiCacheReplicatorFactory().getProperties();
         cacheEventListenerFactoryConfiguration.setProperties(properties);
        return cacheEventListenerFactoryConfiguration;
    }
}

CacheManagerPeerListenerFactoryBuilder

package com.icfcc.fcit.dp.cache.config.ehcache.builder;


import com.icfcc.fcit.dp.cache.properties.EhCacheProperties;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.sf.ehcache.config.FactoryConfiguration;
import net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory;


/**
* @Description: 缓存管理监听工厂建造者
* CacheManagerPeerListener:用来接收集群中其他成员发来的消息
* @Author: gezongyang
* @Date: Created in 2022-11-11 15:37
**/
@Slf4j
@AllArgsConstructor
public class CacheManagerPeerListenerFactoryBuilder implements  Builder<FactoryConfiguration<?>> {

    private static final String CLASS_NAME =  RMICacheManagerPeerListenerFactory.class.getName();
    EhCacheProperties ehCacheProperties;

    /**
     * 针对cacheManager事件的监听,这里只介绍properties中的hostName、port属性,
     * 这里我忽略了hostName的配置,查看他们的源码发现如果不填写hostName,
     * 他们就会通过JDK中的InterAddress.getLocalHost().getHostAddress()获取本机的ip地址,
     * 所以在这里我没有填写hostName的配置,方便部署到多台硬件服务器上。
     * 但是如果一台已经服务器上有多个网卡,这里一定要指定hostName的IP,原因参考InterAddress源码。
     * post这里我指定的时40001,如果这里不填写port配置,ehcache就会通过ServerSocket的getLocalPort获取一个本机没有被占用的端口
     * remoteObjectPort 远程对象绑定到注册表后接收调用的端口号,其他节点可以通过这个端口与当前节点上的ehcache 实例进行通信,以同步缓存数据、处理缓存更新
     **/
     @Override
    public FactoryConfiguration<?> build() {
        return new FactoryConfiguration<FactoryConfiguration<?>>().className(CLASS_NAME)
                .properties("hostName=" + ehCacheProperties.getCluster().getListener().getHostName() + ",port="
                        + ehCacheProperties.getCluster().getListener().getPort() + ",socketTimeoutMillis="
                        + ehCacheProperties.getCluster().getListener().getSocketTimeoutMillis() + ",remoteObjectPort="
                        + ehCacheProperties.getCluster().getListener().getRemoteObjectPort());
    }operties.getCluster().getListener().getSocketTimeoutMillis());
    }
}

CacheManagerPeerProviderFactoryBuilder

package com.icfcc.fcit.dp.cache.config.ehcache.builder;


import com.icfcc.fcit.dp.cache.properties.EhCacheProperties;
import com.icfcc.fcit.dp.cache.util.EhCacheHelper;
import lombok.extern.slf4j.Slf4j;
import net.sf.ehcache.config.Configuration;
import net.sf.ehcache.config.FactoryConfiguration;
import net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory;
import org.springframework.util.StringUtils;
import java.util.Set;

/**
* @Description:缓存成员发现工厂,管理cacheManager对象
* @Author: gezongyang
* @Date: Created in 2022-11-11 15:10
**/
@Slf4j
public class CacheManagerPeerProviderFactoryBuilder implements  Builder<FactoryConfiguration<?>> {

    /**
     * CacheManagerPeerProviderFactory类名
     */
    private static final String CLASS_NAME =  RMICacheManagerPeerProviderFactory.class.getName();

    EhCacheProperties ehCacheProperties;
    /**
     * 发现方式
     */
    String peerDiscovery;

    Set<String> cacheNames;

    public CacheManagerPeerProviderFactoryBuilder(EhCacheProperties  ehCacheProperties) {
        this.ehCacheProperties = ehCacheProperties;
        this.peerDiscovery =  ehCacheProperties.getCluster().getProvider().getPeerDiscovery();
    }

    public CacheManagerPeerProviderFactoryBuilder(EhCacheProperties  ehCacheProperties, Set<String> cacheNames) {
        this(ehCacheProperties);
        this.cacheNames = cacheNames;
    }

    public CacheManagerPeerProviderFactoryBuilder(EhCacheProperties  ehCacheProperties, Configuration configuration) {
        this(ehCacheProperties,  configuration.getCacheConfigurationsKeySet());
    }

    @Override
    public FactoryConfiguration<?> build() {


        if  (EhCacheProperties.Cluster.Provider.PEERDISCOVERY_MANUAL.equals(peerDiscovery)) {
            return manualFactoryConfiguration();
        } else {
            throw new IllegalArgumentException(peerDiscovery);
        }
    }

    /**
     * @Description: 手动发现方式工厂配置
     * @Author: gezongyang
     * @Param: []
     * @Return:  net.sf.ehcache.config.FactoryConfiguration<net.sf.ehcache.config.FactoryConfiguration<?>>
     * @Date: 2022/11/11
     **/
    private FactoryConfiguration<FactoryConfiguration<?>>  manualFactoryConfiguration() {
        String rmiUrls =  ehCacheProperties.getCluster().getProvider().getManual().getRmiUrls();
        if (StringUtils.hasText(rmiUrls)) {
            String makeRmiUrls = new EhCacheHelper().makeRmiUrls(rmiUrls,  cacheNames);
            return new FactoryConfiguration<FactoryConfiguration<?>>()
                    .className(CLASS_NAME)
                    .properties("peerDiscovery=" +  EhCacheProperties.Cluster.Provider.PEERDISCOVERY_MANUAL + ",rmiUrls="
                            + makeRmiUrls);
        }
        throw new NullPointerException("rmiUrls can not be null");
    }

}

EhCacheConfigurationBuilder

package com.icfcc.fcit.dp.cache.config.ehcache.builder;


import com.icfcc.fcit.dp.cache.properties.EhCacheProperties;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.sf.ehcache.config.CacheConfiguration;
import net.sf.ehcache.config.Configuration;
import net.sf.ehcache.config.DiskStoreConfiguration;
import net.sf.ehcache.config.FactoryConfiguration;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* @Description:Ehcache缓存配置建造者
* @Author: gezongyang
* @Date: Created in 2022-11-11 16:14
**/
@Slf4j
@AllArgsConstructor
public class EhCacheConfigurationBuilder implements  Builder<Configuration>{

    private final EhCacheProperties ehCacheProperties;

    @Override
    public Configuration build() {
        DiskStoreConfiguration diskStoreConfiguration = new  DiskStoreConfiguration().path(ehCacheProperties.getPath());
        Configuration configuration = new Configuration();
        configuration.diskStore(diskStoreConfiguration);
        configuration.setName(ehCacheProperties.getDefaultConfigName());
        buildCaches(configuration);
        if (ehCacheProperties.isClusterEnabled()) {
            buildCacheManagerPeerProviderFactory(configuration);
            buildCacheManagerPeerListenerFactory(configuration);
        }
        logConfiguration(configuration);
        return configuration;
    }

    private void buildCaches(Configuration configuration) {
        Map<String, EhCacheProperties.Cache> regions =  ehCacheProperties.getRegions();
        for (Map.Entry<String, EhCacheProperties.Cache> cacheEntry :  regions.entrySet()) {
            CacheConfiguration cacheConfiguration = new  CacheConfigurationBuilder(cacheEntry.getKey(),  ehCacheProperties).build();
            configuration.addCache(cacheConfiguration);
        }
    }

    private void buildCacheManagerPeerProviderFactory(Configuration  configuration) {
        CacheManagerPeerProviderFactoryBuilder  cacheManagerPeerProviderFactoryBuilder = new  CacheManagerPeerProviderFactoryBuilder(ehCacheProperties,  configuration);
         configuration.addCacheManagerPeerProviderFactory(cacheManagerPeerProviderFactoryBuilder.build());
    }

    private void buildCacheManagerPeerListenerFactory(Configuration  configuration) {
        CacheManagerPeerListenerFactoryBuilder  cacheManagerPeerListenerFactoryBuilder = new  CacheManagerPeerListenerFactoryBuilder(ehCacheProperties);
         configuration.addCacheManagerPeerListenerFactory(cacheManagerPeerListenerFactoryBuilder.build());
    }

    private void logConfiguration(Configuration configuration){
        if (!log.isInfoEnabled()) {
            return;
        }
        Map<String, CacheConfiguration> cacheConfigurations =  configuration.getCacheConfigurations();
        Set<Map.Entry<String,CacheConfiguration>> entrySet =  cacheConfigurations.entrySet();
        log.info("EhCache caches:");
        for (Map.Entry<String, CacheConfiguration> entry : entrySet) {
            StringBuilder sb = new StringBuilder();
            sb.append(entry.getKey()).append(":  ").append(entry.getValue());
            log.info(sb.toString());
             List<CacheConfiguration.CacheEventListenerFactoryConfiguration>  cacheEventListenerConfigurations =  entry.getValue().getCacheEventListenerConfigurations();
            for  (CacheConfiguration.CacheEventListenerFactoryConfiguration  factoryConfiguration : cacheEventListenerConfigurations) {
                log.info("     cacheEventListenerFactory:  classPath:{}",factoryConfiguration.getFullyQualifiedClassPath());
                log.info("     properties:{},listenFor:{}",factoryConfiguration.getProperties(),factoryConfiguration.getListenFor());
            }
        }

        List<FactoryConfiguration>  cacheManagerPeerProviderFactoryConfiguration =  configuration.getCacheManagerPeerProviderFactoryConfiguration();
        for (FactoryConfiguration factoryConfiguration :  cacheManagerPeerProviderFactoryConfiguration) {
            log.info("cacheManagerPeerProviderFactory:  classPath:{}",factoryConfiguration.getFullyQualifiedClassPath());
             log.info("properties:{}",factoryConfiguration.getProperties());
        }

        List<FactoryConfiguration>  cacheManagerPeerListenerFactoryConfigurations =  configuration.getCacheManagerPeerListenerFactoryConfigurations();
        for (FactoryConfiguration factoryConfiguration :  cacheManagerPeerListenerFactoryConfigurations) {
            log.info("cacheManagerPeerListenerFactory:  classPath:{}",factoryConfiguration.getFullyQualifiedClassPath());
             log.info("properties:{}",factoryConfiguration.getProperties());
        }
    }
}

EhCacheAutoConfiguration

package com.icfcc.fcit.dp.cache.config.ehcache;
import  com.icfcc.fcit.dp.cache.config.ehcache.builder.EhCacheConfigurationBuilder;
import com.icfcc.fcit.dp.cache.properties.EhCacheProperties;
import com.icfcc.fcit.dp.cache.support.ehcache.IEhCacheManager;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import  org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import  org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import  org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import  org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* gezongyang
* ehcache 自动配置类
*/
@EnableCaching
@Configuration
@EnableConfigurationProperties(value = {EhCacheProperties.class})
@ConditionalOnClass({Cache.class, EhCacheCacheManager.class})
@ConditionalOnMissingBean({org.springframework.cache.CacheManager.class})
@ConditionalOnProperty(name = "type", prefix = "spring.cache",  havingValue = "ehcache")
public class EhCacheAutoConfiguration {

    private final EhCacheProperties ehCacheProperties;
    public EhCacheAutoConfiguration(EhCacheProperties ehCacheProperties)  {
        this.ehCacheProperties = ehCacheProperties;
    }

    @Bean
    IEhCacheManager iEhCacheManager(EhCacheCacheManager  ehCacheCacheManager) {
        return new IEhCacheManager(ehCacheCacheManager);
    }

    @Bean
    public EhCacheCacheManager ehCacheCacheManager(CacheManager  cacheManager) {
        return new EhCacheCacheManager(cacheManager);
    }

    @Bean
    public CacheManager cacheManager() {
        EhCacheConfigurationBuilder ehCacheConfigurationBuilder = new  EhCacheConfigurationBuilder(ehCacheProperties);
        return CacheManager.create(ehCacheConfigurationBuilder.build());
    }
}

EhCacheProperties

package com.icfcc.fcit.dp.cache.properties;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import net.sf.ehcache.config.CacheConfiguration;
import  org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.HashMap;
import java.util.Map;

/**
* gezongyang
* ehcache 配置属性类
*/
@ConfigurationProperties(prefix = "spring.ehcache")
@Slf4j
@Data
public class EhCacheProperties {
    /**
     * 默认配置
     */
    private String defaultConfigName = "common-cache";
    /**
     * 存储地址
     */
    private String path = "/ehcache";
    /**
     * 缓存区域配置
     */
    private Map<String, Cache> regions = new HashMap<>();
    private static final String RESERVED_ADDRESS = "127.0.0.1";
    private static final String split = ":";

    /**
     * 是否启用集群 默认不启用
     */
    private boolean clusterEnabled = false;
    /**
     * 无需同步的缓存名称 |分隔 TODO:
     */
    private String unSyncCacheNames;

    private Cluster cluster = new Cluster();

    /**
     * @Description: 集群配置
     * @Author: gezongyang
     * @Date: 2022/11/11
     **/
    @Data
    public static class Cluster {
        private Provider provider = new Provider();
        private Listener listener = new Listener();
        
        /**
         * @Description: 缓存提供者
         * @Author: gezongyang
         * @Date: 2022/11/11
         **/
        @Data
        public static class Provider {

            public static final String PEERDISCOVERY_MANUAL = "manual";
            /**
             * 缓存提供者rmi通信方式 手动
             */
            private String peerDiscovery = PEERDISCOVERY_MANUAL;
            
            private Manual manual = new Manual();

            @Data
            public static class Manual {

                private static final int DEFAULT_RMI_NOTIFY_PORT = 40001;
                /**
                 * 缓存提供者需要通知的rmi地址 多个 | 分隔 例如:127.0.0.1:40001
                 */
                private String rmiUrls = RESERVED_ADDRESS + split +  DEFAULT_RMI_NOTIFY_PORT;
            }
        }

        /**
         * @Description: 缓存监听者
         * @Author: gezongyang
         * @Date: 2022/11/11
         **/
        @Data
        public static class Listener {

            private static final int DEFAULT_LISTENER_PORT = 40002;
            private static final int DEFAULT_LISTENER_PORT = 40001;
            /**
             * 缓存监听者端口地址 默认:127.0.0.1
             */
            private String hostName = RESERVED_ADDRESS;
            /**
             * 缓存监听者默认端口 默认:40002
             */
            private int port = DEFAULT_LISTENER_PORT;
            /**
             * socket连接超时时间 默认2000毫秒
             */
            private long socketTimeoutMillis = 2000L;
              /**
             * 指定Ehcache实例监听远程方法调用的端口号。这意味着其他节点可以通过这个端口与当前节点上的                     * Ehcache实例进行通信,以同步缓存数据、处理缓存更新等
             */
            private int remoteObjectPort = DEFAULT_REMOTE_OBJECT_PORT;
        }
    }

    @Data
    public static class Cache {
        /**
         * cacheEventListenerFactory子标签:主要用来定义缓存事件监听的处理策略,由以下参数设置其同步策略
         * replicatePuts: 当一个新元素增加到缓存中的时候是否要复制到其他的peers。default:true
         * replicateUpdates: 当一个已经在缓存中存在的元素被覆盖时是否要进行复制。default:true
         * replicateRemovals: 当元素移除的时候是否进行复制。default:true
         * replicateAsynchronously: 复制方式是异步时为true,同步时为false   default:true
         * replicatePutsViaCopy: 当一个新增元素被拷贝到其他的cache中时是否进行复制,true为复制 default:true
         * replicateUpdatesViaCopy: 当一个已存在元素被拷贝到其他cache中时是否进行复制,true为复制 default:true
         * @Author: gezongyang
         * @Date: 2022/11/11
         **/
        public static final String  DEFAULT_RMI_CACHE_REPLICATOR_FACTORY_PROPERTIES_NAME =  "replicatePuts=true,replicateUpdates=true,replicateRemovals=true,replicateAsynchronously=true,replicatePutsViaCopy=true,replicateUpdatesViaCopy=true,asynchronousReplicationIntervalMillis=true";
        public static final String DEFAULT_MEMORY_STORE_EVICTION_POLICY  = "LRU";

       /**
        * 在Cache缓存的element的最大数目,默认值为0,表示不限制
        **/
        long maxEntriesLocalHeap;
        /**
         * 在磁盘上缓存的element的最大数目,默认值为0,表示不限制。
         */
        int maxEntriesLocalDisk;
        /**
         * 对象空闲时间,指对象在多长时间没有被访问就会失效。只对eternal为false的有效。默认值0,表示一直可以访问。以秒为单位。
         */
        long timeToIdleSeconds = 0L;
        /**
         * 对象存活时间,指对象从创建到失效所需要的时间。只对eternal为false的有效。默认值0,表示一直可以访问。以秒为单位。
         */
        long timeToLiveSeconds = 0L;

        /**
         * DiskStore使用的磁盘大小,默认值30MB。每个cache使用各自的DiskStore。
         */
        int diskSpoolBufferSizeMB = 30;

        /**
         * 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,
         * 如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断。
         */
        boolean eternal;

        /**
         * 缓存的3 种清空策略 :

         * FIFO ,first in first out (先进先出).

         * LFU , Less Frequently Used (最少使用).意思是一直以来最少被使用的。缓存的元素有一个hit 属性,hit
         * 值最小的将会被清出缓存。

         * LRU ,Least Recently Used(最近最少使用). (ehcache
         * 默认值).缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,
         * 那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
         */
        String memoryStoreEvictionPolicy =  DEFAULT_MEMORY_STORE_EVICTION_POLICY;

        /**
         * 对象检测线程运行时间间隔。标识对象状态的线程多长时间运行一次。以秒为单位。默认120 秒
         */
        long diskExpiryThreadIntervalSeconds = 120L;

        RMICacheReplicatorFactory rmiCacheReplicatorFactory = new  RMICacheReplicatorFactory();

        @Data
        public static class RMICacheReplicatorFactory {
            String properties =  DEFAULT_RMI_CACHE_REPLICATOR_FACTORY_PROPERTIES_NAME;
        }
    }
}

2.6 配置文件详解

ehcache:
  path: e:\\temp
  default-config-name: common-cache
  cluster-enabled: true
  cluster:
    provider:
      peer-discovery: manual
      manual:
        rmi-urls: 10.1.4.44:40002  #缓存提供者需要通知的rmi地址 多个 | 分隔  配置n-1
    listener:
      host-name: 10.1.4.16  #缓存监听者端口地址 默认:127.0.0.1
      port: 40002
      socket-timeout-millis: 2000 #监听socket连接超时时间 默认2000毫秒
      remote-object-port: 40001 #远程对象绑定到注册表后接收调用的端口号,其他节点可以通过这个端口与当前节点上的ehcache 实例进行通信,以同步缓存数据、处理缓存更新
  regions:
    common-cache:
      #Sets the maximum objects to be held in local heap memory (0 = no  limit)
      max-entries-local-heap: 0
      #Sets the maximum number elements on Disk
      max-entries-local-disk: 100000000
      time-to-idle-seconds: 1800
      time-to-live-seconds: 1800
      #this is the size to allocate the DiskStore for a spool buffer
      disk-spool-buffer-size-m-b: 30
      eternal: false
      #Sets the eviction policy
      memory-store-eviction-policy: LRU
      #Sets the interval in seconds between runs of the disk expiry  thread
      disk-expiry-thread-interval-seconds: 120

注意:因为rmi 要在远程对象上调用方法,RMI客户端首先必须从RMI注册表中检索远程存根对象。默认情况下,服务器将尝试检测自己的地址并将其传递给存根对象,但是当单台服务器中有虚拟机或者服务器多网卡时,返回给存根对象的地址可能会错误(如返回虚拟机地址),出现连接超时异常。通过设置RMI服务器上的系统属性java.rmi.server.hostname,可以覆盖传递给存根对象的服务器地址。

-Djava.rmi.server.hostname=<<rmi server ip>>  

-Djava.rmi.server.hostname=10.1.4.16

2.7 分布式缓存EHCACHE系统PEER-2-PEER的缓存同步上存在的问题:

RMI p2p方式:( 其实就是每个节点和其他n-1个节点都建立TCP的P2P PEER
P2P要求每个节点的EHCACHE要指向其他的N-1个节点, 当在云环境,或集群域下, 多个子节点部署项目都是被自动发布的,这时很难做到不同节点有不同的配置,这样P2P就很难实现. 总之,这种同步型应用是很难适应大规模分布式部署,在大规模分布式部署建议采用一些集中软件比如REDIS、MEMCACHED。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

独行客-编码爱好者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值