Ehcache2详解

18 阅读15分钟

一,Ehcache2介绍

Ehcache 2 是一个广泛使用的 Java 缓存库,用于提升应用程序的性能和响应速度。它支持多种缓存策略和配置选项,使得在Java应用中实现缓存变得更加容易。


关键特性:

  • 缓存存储
    • 内存缓存:数据存储在内存中,提供快速访问。
      • heap内存:该层受 Java 垃圾收集的约束,在JVM内存中开辟的内存
      • offHeap内存:不受 Java 垃圾收集 (GC) 的影响,在JVM外开辟的内存
    • 磁盘缓存:当内存缓存满时,数据可以被写入磁盘,以节省内存并持久化数据。
  • 缓存策略
    • 过期策略:支持基于时间的过期策略(如 TTL 和 TTI),确保缓存的数据在一定时间后过期。
    • LRU(最近最少使用):当缓存达到最大容量时,最少使用的数据会被移除。
  • 缓存持久化
    • 内存和磁盘持久化:可以将缓存内容持久化到磁盘,以便在应用重启后恢复数据。
  • 分布式缓存
    • 集群支持:Ehcache 可以通过 Terracotta 的集群支持实现分布式缓存。
  • 灵活的配置
    • XML配置:支持使用 XML 文件进行详细配置。
    • 程序化配置:可以通过 Java 代码进行配置。

组件:

  • CacheManager:缓存管理器,负责管理所有的缓存实例。应用程序通常只有一个 CacheManager 实例,用于创建和配置缓存。
  • Cache:缓存对象,包含缓存数据和缓存策略的实现。每个 Cache 对象负责具体的数据存储和过期策略。
  • CacheLoader:用于从持久存储中加载缓存数据的接口。可以实现自定义的加载逻辑。
  • CacheWriter:用于将缓存数据写入持久存储的接口。

二,快速入门

  1. 添加依赖

    <!--Ehcache2-->
    <dependency>
        <groupId>org.ehcache</groupId>
        <artifactId>ehcache</artifactId>
        <version>2.10.9</version>
    </dependency>
    
  2. 配置Ehcache

    创建一个 Ehcache 配置文件 ehcache.xml,放置在 src/main/resources 目录下。

    <ehcache>
        <!-- 定义磁盘存储路径为 d:/temp -->
        <diskStore path="d:/temp"/>
        <!--
            maxEntriesLocalHeap:堆内存中最多存储 10000 个缓存条目
            eternal:缓存条目不是永久有效的,会根据TTL或TTI过期
            timeToIdleSeconds: 缓存条目在 120 秒内未被访问将过期
            timeToLiveSeconds:缓存条目在 120 秒后无论是否被访问都将过期
            maxEntriesLocalDisk:磁盘中最多存储 10000000 个缓存条目
            diskExpiryThreadIntervalSeconds: 磁盘清理线程的运行间隔时间为 120 秒
            memoryStoreEvictionPolicy: 堆内存中的条目使用 LRU(最近最少使用)策略进行驱逐
        -->
        <!-- 名为 "myCache" 的具体缓存配置 -->
        <cache name="myCache"
               maxEntriesLocalHeap="10000"
               eternal="false"
               timeToIdleSeconds="120"
               timeToLiveSeconds="120"
               maxEntriesLocalDisk="10000000"
               diskExpiryThreadIntervalSeconds="120"
               memoryStoreEvictionPolicy="LRU">
            <persistence strategy="localTempSwap"/>
        </cache>
    </ehcache>
    
  3. 配置Ehcache的bean

    import net.sf.ehcache.Cache;
    import net.sf.ehcache.CacheManager;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class EhcacheConfig {
        @Bean
        public CacheManager cacheManager() {
            //加载配置文件
            return new CacheManager(getClass().getClassLoader().getResourceAsStream("ehcache.xml"));
        }
        @Bean
        public Cache myCache(CacheManager cacheManager) {
            //返回可手动操作的cache的bean,方便进行一些手动加入缓存和删除缓存
            return cacheManager.getCache("myCache");
        }
    }
    
  4. 注入并使用缓存

    @Slf4j
    @SpringBootTest
    public class SpringBoot2AdminTests {
        @Autowired
        private Cache myCache;
        @Test
        public void testCache(){
            Element element = new Element("cacheKye","cacheValue");
            myCache.put(element);
            Element element1 = myCache.get("cacheKye");
            log.info("element:{}",element1);
        }
    }
    

三,核心API介绍

Ehcache 由一个 CacheManager 组成,它管理以 Caches 形式表示的逻辑数据集,一个 Cache 对象包含 Elements,这些 Elements 本质上是 名称-值对。你可以使用 Cache 对象来保存任何你想保存在内存中的数据。

缓存的物理实现可以是内存中的缓存或磁盘上的缓存。这些组件的逻辑表示主要通过以下类实现:

  • CacheManager:缓存管理器
  • Cache:缓存
  • Element:元素

这些类构成了Ehcache API的核心。这些类提供的方法主要负责提供对缓存或内存数据存储的编程访问。

3.1 CacheManager缓存管理器

CacheManager 类用于管理缓存。缓存的创建、访问和删除由命名的 CacheManager 控制。

缓存管理器(CacheManager)支持两种创建模式:

  1. 单例模式(singleton)
  2. 实例模式(instance)

这两种类型可以在同一个JVM中共存。然而,同一个JVM中不允许存在多个同名的缓存管理器。使用 CacheManager() 构造函数创建的非单例模式缓存管理器可能会违反这一规则,从而导致 NullPointerException。如果你的代码可能在同一个JVM中创建多个同名的缓存管理器,为了避免这种错误,建议使用静态的 CacheManager.create() 方法。该方法会执行以下操作:

  • 如果指定名称(或默认名称)的缓存管理器已经存在,CacheManager.create() 方法会返回该缓存管理器。
  • 如果指定名称(或默认名称)的缓存管理器不存在,CacheManager.create() 方法会创建一个新的缓存管理器。

对于单例模式(singleton),调用 CacheManager.create(...) 方法会:

  • 返回具有配置名称的现有单例缓存管理器(如果它已经存在)。
  • 如果指定的单例缓存管理器尚不存在,则根据传入的配置创建一个新的单例缓存管理器。

要从配置中工作,可以使用 CacheManager.newInstance(...) 方法,该方法解析传入的配置,以获取现有的指定名称的缓存管理器,或在缓存管理器不存在时创建它。

CacheManager 创建方法的行为如下:

  • CacheManager.newInstance(Configuration configuration) – 创建一个新的缓存管理器,或返回配置中指定名称的现有缓存管理器。
  • CacheManager.create() – 创建一个具有默认配置的新单例缓存管理器,或返回现有的单例缓存管理器。这与 CacheManager.getInstance() 相同。
  • CacheManager.create(Configuration configuration) – 使用传入的配置创建一个单例缓存管理器,或返回现有的单例缓存管理器。
  • new CacheManager(Configuration configuration) – 创建一个新的缓存管理器,或在配置中指定的缓存管理器已存在时抛出异常,或者当参数(配置)为空时抛出异常。

请注意,在实例模式(非单例模式)下,可以在同一个JVM中同时创建和使用多个缓存管理器,每个缓存管理器都需要自己的配置。

如果受管理的缓存使用磁盘存储,则每个缓存管理器配置中指定的磁盘存储路径应该是唯一的。这是因为在创建新的缓存管理器时,会检查是否有其他缓存管理器使用相同的磁盘存储路径。根据你的持久化策略,Ehcache 将自动解决磁盘存储路径冲突,或者通知你必须显式配置磁盘存储路径。

如果管理的缓存仅使用内存存储,则没有特殊考虑。

3.2 Cache缓存

缓存(Cache) 是一个线程安全的逻辑表示数据元素集合的结构,类似于许多缓存系统中的缓存区域。一旦获得了对缓存的引用(通过 CacheManager),就可以执行逻辑操作。这些操作的具体实现则由存储(stores)负责。

缓存的实例化 可以通过配置文件或使用 Cache() 构造函数程序完成。某些缓存特性,例如自动资源控制(ARC)相关的大小调整和固定(pinning),必须通过配置进行设置。

缓存方法 可用于获取有关缓存的信息(例如,getCacheManager()isNodeBulkLoadEnabled()isSearchable()),或执行某些缓存范围的操作(例如,flushloadinitializedispose)。

Cache 类提供的方法 还允许你操作缓存元素(例如,getsetremovereplace),以及获取有关这些元素的信息(例如,isExpiredisPinned)。

3.3 Element元素

元素(Element) 是缓存中的一个原子条目。它包含一个键、一个值和一个访问记录。元素可以被放入缓存中,也可以从缓存中移除。根据缓存的设置,元素还可以过期并被缓存自动移除。

除了针对可序列化(Serializable)的对象外,还有针对对象(Object)的 API。不可序列化的对象只能存储在堆内存中。如果尝试将它们持久化,将会被丢弃,并记录一个 DEBUG 级别的日志消息,但不会抛出错误。

API 说明:

  • 对象 API 的方法与序列化 API 相同,唯一的区别是 Element 类中的返回方法:getKeyValue()getObjectValue() 用于对象 API,取代了 getKey()getValue()

四,对缓存的操作

4.1 创建CacheManager

所有 Ehcache API 的使用都从创建 CacheManager 开始。以下代码片段展示了几种不同的创建方式:

单例模式(Singleton)与实例模式(Instance)

  1. 单例模式(Singleton): 使用默认配置创建一个单例 CacheManager,然后列出缓存。

    CacheManager.create(); 
    String[] cacheNames = CacheManager.getInstance().getCacheNames();
    
  2. 实例模式(Instance): 使用默认配置创建一个 CacheManager 实例,然后列出缓存。

    CacheManager manager = CacheManager.newInstance(); 
    String[] cacheNames = manager.getCacheNames();
    
  3. 不同配置的多个实例: 创建两个 CacheManager,每个使用不同的配置文件,然后列出每个 CacheManager 中的缓存。

    CacheManager manager1 = CacheManager.newInstance("src/config/ehcache1.xml"); 
    CacheManager manager2 = CacheManager.newInstance("src/config/ehcache2.xml"); 
    String[] cacheNamesForManager1 = manager1.getCacheNames(); 
    String[] cacheNamesForManager2 = manager2.getCacheNames();
    

4.2 加载xml配置文件

在创建 CacheManager 时,可以根据我们提供的xml配置文件加载配置

  1. 使用类路径中的默认配置文件创建 CacheManager

    CacheManager manager = CacheManager.newInstance();
    

    默认路径是:/src/resource/ehcache.xml

  2. 使用指定的配置文件创建 CacheManager

    CacheManager manager = CacheManager.newInstance("src/config/ehcache.xml");
    
  3. 从类路径中的配置资源创建 CacheManager

    URL url = getClass().getResource("/ehcache.xml"); 
    CacheManager manager = CacheManager.newInstance(url);
    
  4. InputStream 中的配置创建 CacheManager

    InputStream fis = new FileInputStream(new File("src/config/ehcache.xml").getAbsolutePath()); 
    try { 
        CacheManager manager = CacheManager.newInstance(fis); 
    } finally { 
        fis.close(); 
    }
    

4.3 以编程方式添加和删除Cache

4.3.1 添加缓存

  1. 使用默认配置添加缓存

    你可以使用默认配置将新缓存添加到 CacheManager 中。以下代码将一个名为 testCache 的缓存添加到 singletonManager 中:

    CacheManager singletonManager = CacheManager.create(); 
    singletonManager.addCache("testCache"); 
    Cache test = singletonManager.getCache("testCache");
    
  2. 使用自定义配置创建并添加缓存

    创建一个使用自定义配置的缓存,并将其添加到 CacheManager 中。注意,缓存在添加到 CacheManager 之前不可用:

    CacheManager singletonManager = CacheManager.create(); 
    Cache memoryOnlyCache = new Cache("testCache", 5000, false, false, 5, 2); 
    singletonManager.addCache(memoryOnlyCache); 
    Cache test = singletonManager.getCache("testCache");
    
  3. 使用 CacheConfiguration 创建并添加缓存

    通过指定缓存配置创建缓存,并将其添加到 CacheManager

    // 使用默认配置创建单例 CacheManager
    CacheManager manager = CacheManager.create(); 
    
    // 创建指定配置的缓存
    Cache testCache = new Cache(
      new CacheConfiguration("testCache", maxEntriesLocalHeap)
        .memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.LFU)
        .eternal(false)
        .timeToLiveSeconds(60)
        .timeToIdleSeconds(30)
        .diskExpiryThreadIntervalSeconds(0)
        .persistence(new PersistenceConfiguration().strategy(Strategy.LOCALTEMPSWAP))
    ); 
    manager.addCache(testCache);
    

4.3.2 删除缓存

使用 CacheManager 删除名为 sampleCache1 的缓存:

CacheManager singletonManager = CacheManager.create(); 
singletonManager.removeCache("sampleCache1");

4.4 缓存基本操作

以下示例中,manager 是指向一个包含名为 sampleCache1 的缓存的 CacheManager 对象。

4.4.1 获取缓存引用

获取名为 sampleCache1 的缓存,该缓存已在配置文件中预配置:

Cache cache = manager.getCache("sampleCache1");

4.4.2 将元素放入缓存

将一个元素放入缓存中:

Cache cache = manager.getCache("sampleCache1"); 
Element element = new Element("key1", "value1"); 
cache.put(element);

4.4.3 更新缓存中的元素

更新缓存中的一个元素。尽管使用了 cache.put() 方法,Ehcache 知道已有元素存在,因此将 put 操作视为更新,以便通知缓存监听器:

Cache cache = manager.getCache("sampleCache1"); 
cache.put(new Element("key1", "value1")); 
// 更新 "key1" 的条目 
cache.put(new Element("key1", "value2"));

4.4.4 从缓存中获取元素

  • 从键为 key1 的元素中获取一个可序列化的值:

    Cache cache = manager.getCache("sampleCache1"); 
    Element element = cache.get("key1"); 
    Serializable value = element.getValue();
    
  • 从键为 key1 的元素中获取一个不可序列化的值:

    Cache cache = manager.getCache("sampleCache1"); 
    Element element = cache.get("key1"); 
    Object value = element.getObjectValue();
    

4.4.5 从缓存中移除元素

移除键为 key1 的元素:

Cache cache = manager.getCache("sampleCache1"); 
cache.remove("key1");

4.4.6 获取缓存大小

  • 获取当前缓存中的元素数量:

    Cache cache = manager.getCache("sampleCache1"); 
    int elementsInMemory = cache.getSize();
    
  • 获取当前内存存储中的元素数量:

    Cache cache = manager.getCache("sampleCache1"); 
    long elementsInMemory = cache.getMemoryStoreSize();
    
  • 获取当前磁盘存储中的元素数量:

    Cache cache = manager.getCache("sampleCache1"); 
    long elementsInMemory = cache.getDiskStoreSize();
    

4.5 关闭CacheManager

使用完 CacheManager 后应当将其关闭。虽然它有一个关闭钩子,但最好在代码中关闭一下。

  • 关闭单例 CacheManager

    CacheManager.getInstance().shutdown();
    
  • 关闭 CacheManager 实例

    manager.shutdown();
    

五,XML配置详解

5.1 XML配置介绍

默认情况下,Ehcache 会在 Java 类路径的顶层查找名为 ehcache.xml 的 ASCII 或 UTF8 编码的 XML 配置文件。你可以通过使用各种 CacheManager 构造函数来指定替代路径和文件名,

为了避免资源冲突,每个创建的 CacheManager 需要一个 XML 配置。例如,目录路径和监听端口需要唯一值。Ehcache 会尝试解决冲突,如果发现冲突,会发出警告,提醒用户为多个 CacheManager 使用不同的配置。

ehcache-failsafe.xml

如果调用 CacheManager 的默认构造函数或工厂方法,Ehcache 会在类路径顶层查找名为 ehcache.xml 的文件。如果找不到,它会查找类路径中的 ehcache-failsafe.xmlehcache-failsafe.xml 文件打包在 Ehcache JAR 中,应始终能够找到。

ehcache-failsafe.xml 提供了一个简单的默认配置,帮助用户在创建自己的 ehcache.xml 之前快速启动。

使用 ehcache-failsafe.xml 时,Ehcache 会发出警告,提醒用户设置适当的配置。元素和属性的含义在 ehcache.xml 部分中进行了说明。

示例配置:

<ehcache> 
  <diskStore path="java.io.tmpdir"/> 
  <defaultCache 
     maxEntriesLocalHeap="10000" 
     eternal="false" 
     timeToIdleSeconds="120" 
     timeToLiveSeconds="120" 
     maxEntriesLocalDisk="10000000" 
     diskExpiryThreadIntervalSeconds="120" 
     memoryStoreEvictionPolicy="LRU"> 
     <persistence strategy="localTempSwap"/> 
  </defaultCache> 
</ehcache>

defaultCache 配置应用于任何未显式配置的缓存。defaultCache 默认出现在 ehcache-failsafe.xml 文件中,也可以添加到任何 Ehcache 配置文件中。

虽然 defaultCache 配置不是必需的,但如果程序创建缓存时没有加载 defaultCache,会产生错误。

5.2 存储层特点

Ehcache 提供三种存储层,每种层级有其特定的用途和特性:

  1. 内存存储(Memory store)
    • 受 Java 垃圾回收(GC)影响。
    • 接受所有数据,无论是否可序列化
    • 线程安全,适合多个并发线程使用
    • 最快的存储选项
  2. 堆外存储(Off-heap store)
    • 大小仅受可用 RAM 限制。
    • 不受 Java GC 影响。
    • 只能存储序列化的数据。
    • 提供内存存储的溢出容量,确保缓存能够存储更多数据。
  3. 磁盘存储(Disk store)
    • 备份内存数据,并为其他存储层提供溢出容量。
    • 只能存储序列化的数据。
    • 适用于长期存储和大规模数据的持久化。

5.3 动态更改缓存配置

自 Ehcache 2.0 以来,某些缓存配置参数可以在运行时动态修改。这些包括:

  1. 过期设置

    • timeToLive(生存时间):元素在缓存中存在的最大秒数,无论是否被访问。达到此限制后,元素过期,将不再从缓存中返回。默认值为 0,表示没有 TTL 驱逐(无限生命周期)。

    • timeToIdle(闲置时间):元素在缓存中存在的最大秒数,而不被访问。达到此限制后,元素过期,将不再从缓存中返回。默认值为 0,表示没有 TTI 驱逐(无限生命周期)。

    • 注意:当 eternal 属性设置为 true 时,会覆盖 timeToLivetimeToIdle,使元素不会过期。

  2. 本地大小属性

    • maxEntriesLocalHeap:本地堆中的最大条目数。
    • maxBytesLocalHeap:本地堆中的最大字节数。
    • maxEntriesLocalDisk:本地磁盘中的最大条目数。
    • maxBytesLocalDisk:本地磁盘中的最大字节数。
  3. 内存存储驱逐策略

  4. CacheEventListeners 可以动态添加和移除

举例:

public static void main(){
    CacheManager cacheManage=new CacheManager();
    // 创建Cache配置对象
    CacheConfiguration cacheConfiguration = new CacheConfiguration();
    // 配置缓存的名字为 "myCache"
    cacheConfiguration.setName("myCache");
    // 设置堆内存中最多存储 1000 个缓存条目
    cacheConfiguration.setMaxEntriesLocalHeap(1000);
    // 设置磁盘中最多存储 10000 个缓存条目
    cacheConfiguration.setMaxEntriesLocalDisk(10000);
    // 设置缓存条目在 60 秒内未被访问将过期
    cacheConfiguration.setTimeToIdleSeconds(60);
    // 设置缓存条目在 120 秒后无论是否被访问都将过期
    cacheConfiguration.setTimeToLiveSeconds(120);
    // 设置缓存对象的清除策略为 LFU(最少使用策略)
    cacheConfiguration.setMemoryStoreEvictionPolicy("LFU");
    // 设置磁盘清理线程的运行间隔时间为 120 秒
    cacheConfiguration.setDiskExpiryThreadIntervalSeconds(120);
    // 设置缓存条目不是永久有效的
    cacheConfiguration.setEternal(false);
    // 创建持久化配置对象
    PersistenceConfiguration persistenceConfiguration = new PersistenceConfiguration();
    // 设置持久化策略为 LOCALTEMPSWAP(本地临时交换)
    persistenceConfiguration.setStrategy("LOCALTEMPSWAP");
    // 将持久化配置应用到缓存配置中
    cacheConfiguration.persistence(persistenceConfiguration);
    //创建使用cacheConfiguration的缓存
    Cache cache = new Cache(cacheConfiguration());
    //让CacheManager添加这个缓存
    cacheManager.addCache(cache);
    //获得该缓存
    Cache myCache=cacheManager.getCache("myCache");
}

禁用动态配置

  • API方式

    Cache cache = manager.getCache("sampleCache"); 
    cache.disableDynamicFeatures();
    
  • ehcache.xml 文件中,你可以通过将 <ehcache> 元素的 dynamicConfig 属性设置为 false 来禁用动态配置:

    <ehcache dynamicConfig="false">
      <!-- 配置内容 -->
    </ehcache>
    

5.4 缓存对象清除策略

<ehcache> 
  <defaultCache 
     memoryStoreEvictionPolicy="LRU"> 
</ehcache>

针对上述memoryStoreEvictionPolicy配置有如下取值:

  • FIFO(First In First Out):先进先出
  • LFU(Least Frequently Used):最少使用,使用次数最少的条目将被清理
  • LRU(Least Recenly Used):最近最少使用,最近一段时间内使用次数最少的条目将被清理

5.5 缓存持久化策略

<ehcache> 
  <diskStore path="java.io.tmpdir"/> 
  <defaultCache>
     <persistence strategy="localTempSwap"/> 
  </defaultCache>
</ehcache>

针对persistence里面的strategy属性配置持久化策略有:

  • localTempSwap:当内存中的条目已经满了的时候,将条目临时存放在磁盘上,一旦重启就会消失。
  • localRestartable:该策略只对企业版 Ehcache 有用,它可以将内存里面的条目持久化到硬盘上,重启之后再从硬盘上恢复到内存中。
  • none:不持久化缓存的条目。
  • distributed:该策略是用于分布式情况下的。

5.6 模板配置

给出ehcache.xml的模板配置,剩下的根据配置的注释进行复制黏贴进行修改即可

<ehcache dynamicConfig="false">
    <!-- 定义磁盘存储路径为 ../temp -->
    <diskStore path="../temp"/>
    <!--
        maxEntriesLocalHeap:堆内存中最多存储 10000 个缓存条目
        eternal:缓存条目不是永久有效的,会根据TTL或TTI过期
        timeToIdleSeconds: 缓存条目在 120 秒内未被访问将过期
        timeToLiveSeconds:缓存条目在 120 秒后无论是否被访问都将过期
        maxEntriesLocalDisk:磁盘中最多存储 10000000 个缓存条目
        diskExpiryThreadIntervalSeconds: 磁盘清理线程的运行间隔时间为 120 秒
        memoryStoreEvictionPolicy: 堆内存中的条目使用 LRU(最近最少使用)策略进行驱逐
    -->
    <!--配置默认缓存-->
    <defaultCache 
     	maxEntriesLocalHeap="10000" 
     	eternal="false" 
     	timeToIdleSeconds="120" 
     	timeToLiveSeconds="120" 
     	maxEntriesLocalDisk="10000000" 
    	 diskExpiryThreadIntervalSeconds="120" 
    	 memoryStoreEvictionPolicy="LRU"> 
     <persistence strategy="localTempSwap"/> 
        
  </defaultCache> 
    <!-- 名为 "localCache" 的具体缓存配置 -->
    <cache name="localCache"
           maxEntriesLocalHeap="10000"
           eternal="false"
           timeToIdleSeconds="120"
           timeToLiveSeconds="120"
           maxEntriesLocalDisk="10000000"
           diskExpiryThreadIntervalSeconds="120"
           memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </cache>
</ehcache>