Ehcache3

0 阅读6分钟

一,引言

Ehcache 3 是 Ehcache 缓存解决方案的第三个主要版本,是一个开源的 Java 缓存库,提供了灵活、高效的缓存机制。它是 Java 生态系统中广泛使用的缓存库之一,用于加速应用程序性能和降低数据访问延迟。Ehcache 3 具有许多功能和改进,使其适用于现代应用程序的需求。

1.1 主要组件

  1. CacheManager:
    • 描述: CacheManager 是 Ehcache 的核心组件之一,负责管理一个或多个缓存。它创建和配置缓存,并且可以通过配置文件或编程方式进行设置。
    • 功能: 管理缓存的生命周期、配置缓存、管理缓存的存储策略。
  2. Cache:
    • 描述: Cache 是 Ehcache 的核心数据结构,用于存储缓存条目。每个缓存由 CacheManager 管理,并根据配置处理数据的存储和过期策略。
    • 功能: 存储数据、应用缓存策略(如过期、驱逐)、支持同步和异步操作。
  3. CacheConfiguration:
    • 描述: CacheConfiguration 用于定义缓存的配置,如容量限制、过期策略、存储方式等。
    • 功能: 配置缓存的属性,包括存储层级、过期策略、驱逐策略等。
  4. CacheLoader:
    • 描述: CacheLoader 用于从数据源加载数据到缓存中。它是缓存的一个重要组成部分,用于数据填充和初始加载。
    • 功能: 实现数据的加载逻辑,用于缓存的初始化和数据填充。
  5. CacheWriter:
    • 描述: CacheWriter 用于将缓存的数据写入外部数据源。它允许缓存与外部存储进行交互。
    • 功能: 实现缓存数据的写入逻辑,确保数据一致性和持久化。
  6. CacheEventListener:
    • 描述: CacheEventListener 用于监听缓存的事件(如添加、更新、删除)。
    • 功能: 实现事件处理逻辑,监控缓存状态变化,进行相应的处理。

1.1 功能特性

  1. 多级存储支持:
    • 内存存储: 提供最快的数据访问速度,存在 Java 堆内存中。
    • 堆外存储: 在内存外进行存储,提供额外的容量,避免 GC 暂停。
    • 磁盘存储: 用于数据的持久化,提供额外的溢出空间。
  2. 缓存策略:
    • 过期策略: 支持基于时间的过期策略,如 TTL(生存时间)和 TTI(空闲时间)。
    • 驱逐策略: 提供多种驱逐策略,如 LRU(最近最少使用)、LFU(最少使用)和 FIFO(先进先出)。
  3. 持久化和溢出:
    • 持久化: 数据可以持久化到磁盘以防丢失,支持持久化策略如 localTempSwap
    • 溢出: 数据可以溢出到堆外存储或磁盘存储,以扩展缓存容量。
  4. 动态配置:
    • 动态修改: 支持在运行时动态修改缓存的配置,如过期时间、最大条目数等。
  5. 集成支持:
    • JCache (JSR-107) 集成: 支持 JCache 标准,允许与其他遵循 JCache 标准的缓存库进行互操作。
    • Spring 集成: 与 Spring 框架无缝集成,支持 Spring 配置和管理缓存。
  6. 高效性能:
    • 线程安全: 内存存储是线程安全的,支持多线程并发操作。
    • 高效存储: 提供优化的数据存储和检索机制,减少延迟和提高性能。
  7. 监控和管理:
    • JMX 支持: 支持通过 JMX 进行缓存监控和管理。
    • 事件监听: 提供事件监听机制,允许开发人员对缓存操作进行定制处理。

二,Ehcache3快速入门

  1. 引入依赖

    <!-- Ehcache 3.x 核心库 -->
    <dependency>
        <groupId>org.ehcache</groupId>
        <artifactId>ehcache</artifactId>
        <version>3.10.8</version> 
    </dependency>
    
  2. 编写配置文件

    Ehcache支持API方式编辑配置文件也支持XML方式编辑配置文件,建议采用XML配置

    • API方式配置

      @Configuration
      public class EhcacheConfig {
          //持久化CacheManager,要存储本地必须用这个
      	private PersistentCacheManager cacheManager;
          /**
           * 配置CacheManager
           */
          @PostConstruct
          public void init() {
              cacheManager= CacheManagerBuilder.newCacheManagerBuilder()
                      // 持久化到磁盘的路径
                      .with(CacheManagerBuilder.persistence("../temp"))
                      //立即初始化
                      .build(true);
          }
      
          /**
           * 创建并配置名为 "localCache" 的缓存
           * @return localCache 缓存实例
           */
          @Bean
          public Cache<String, String> localCache() {
              // 创建并配置缓存 "myCache"
              return cacheManager.createCache("localCache",
                      //设置key和value的类型
                      CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, String.class,
                              // 设置堆内存大小为100个条目
                              ResourcePoolsBuilder.heap(100)));
          }
      }
      
      
    • XML方式配置

      在xml配置文件中我们手动配置一个Cache

      <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xmlns="http://www.ehcache.org/v3"
              xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/v3/schema/ehcache-config-3.0.xsd">
          <!-- 缓存配置 -->
          <cache alias="myCache">
              <key-type>java.lang.String</key-type>
              <value-type>java.lang.String</value-type>
              <!-- 使用条目数配置堆内存大小 -->
              <heap unit="entries">100</heap>
          </cache>
      </config>
      

      获取xml并初始化

      import org.ehcache.Cache;
      import org.ehcache.core.EhcacheManager;
      import org.ehcache.xml.XmlConfiguration;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      
      import javax.annotation.PostConstruct;
      
      /**
       * @author normaling
       */
      @Configuration
      public class EhcacheConfig {
          //直接使用这个实现类更方便
          private EhcacheManager cacheManager;
          /**
           * 配置CacheManager,从XML文件加载配置
           */
          @PostConstruct
          public void init() {
              // 从 XML 配置文件创建 CacheManager
              XmlConfiguration xmlConfig = new XmlConfiguration(getClass().getResource("/ehcache.xml"));
              cacheManager=new EhcacheManager(xmlConfig);
              cacheManager.init();
          }
      
          /**
           * 获取并配置名为 "localCache" 的缓存
           * @return myCache 缓存实例
           */
          @Bean
          public Cache<String, String> localCache() {
              // 从 CacheManager 获取已定义的缓存
              return cacheManager.getCache("localCache", String.class, String.class);
          }
      }
      
  3. 对Cache进行crud操作

    @Slf4j
    @SpringBootTest
    class SpringBoot2DemoApplicationTests {
        @Autowired
        private Cache<String, String> localCache;
        @Test
        void testCache() {
            localCache.put("key", "value");
            //查询key
            String s = localCache.get("key");
            System.out.println(s);
            //多次多同一个key进行put就会更新
            localCache.put("key", "value2");
            //删除key
            localCache.remove("key");
        }
    }
    

三,xml配置模板

<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://www.ehcache.org/v3"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.ehcache.org/v3
        http://www.ehcache.org/schema/ehcache-core-3.10.xsd">
    <!--持久化存储的目录-->
    <persistence directory="..\temp"/>
    <!--cache-template:缓存设置模板-->
    <cache-template name="default">
        <!--key的类型-->
        <key-type>java.lang.String</key-type>
        <!--value的类型-->
        <value-type>java.lang.String</value-type>
        <!--缓存过期配置-->
        <expiry>
            <!--tti:缓存中条目的最大空闲时间-->
            <!--ttl:缓存中条目的最大存活时间-->
            <!--ttl和tti只能配置一个-->
            <!--<tti unit="minutes">5</tti>-->
            <ttl unit="seconds">30</ttl>
        </expiry>
        <!--资源配置-->
        <resources>
            <!--堆内存配置-->
            <heap unit="entries">10</heap>
            <!--堆外存配置-->
            <offheap unit="MB">20</offheap>
            <!--磁盘配置,persistence:是否持久化-->
            <disk persistent="true" unit="MB">50</disk>
        </resources>
    </cache-template>

    <!--创建本地缓存,并且直接调用缓存模板的内容-->
    <cache alias="localCache" uses-template="default">

    </cache>


</config>

四,API配置模板

4.1 方式一

@Configuration
public class EhcacheConfig {
    private PersistentCacheManager cacheManager;
    /**
     * 配置CacheManager
     */
    @PostConstruct
    public void init() {
        // 创建Ehcache CacheManager
        cacheManager= CacheManagerBuilder.newCacheManagerBuilder()
                // 持久化到磁盘的路径
                .with(CacheManagerBuilder.persistence("../temp"))
                //立即初始化
                .build(true);
    }

    /**
     * 创建并配置名为 "localCache" 的缓存
     * @return myCache 缓存实例
     */
    @Bean
    public Cache<String, String> localCache() {
        //创建缓存存储配置
        ResourcePools resourcePools = ResourcePoolsBuilder.newResourcePoolsBuilder()
                // 堆内存缓存
                .heap(10)
                // 堆内外存缓存
                .offheap(20, MemoryUnit.MB)
                // 磁盘缓存
                .disk(30, MemoryUnit.MB, true)
                .build();
        // 创建缓存配置
        CacheConfiguration<String, String> cacheConfiguration = CacheConfigurationBuilder
                //配置缓存key,value的类型和缓存的保存大小
                .newCacheConfigurationBuilder(String.class, String.class, resourcePools)
                //配置缓存过期策略tti
                .withExpiry(ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofSeconds(100)))
                //配置缓存过期策略ttl
                .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(500)))
                .build();

        return cacheManager.createCache("localCache", cacheConfiguration);
    }
}

4.2 方式二

/**
 * 本地Ehcache配置
 * @author normaling
 */
@Slf4j
@Configuration
public class EhcacheConfig {

    private PersistentCacheManager cacheManager;
    /**
     * 配置CacheManager
     */
    @PostConstruct
    public void init() {
        // 创建Ehcache CacheManager
        cacheManager= CacheManagerBuilder.newCacheManagerBuilder()
                // 持久化到磁盘的路径
                .with(CacheManagerBuilder.persistence("../temp"))
                //立即初始化
                .build(true);
    }

    /**
     * 创建并配置名为 "localCache" 的缓存
     * @return localCache 缓存实例
     */
    @Bean
    public Cache<String, String> localCache() {
        return cacheManager.createCache("localCache", cacheConfiguration(String.class, String.class));
    }

    /**
     * 通用默认的缓存配置
     * @param keyType 缓存的key类型
     * @param valueType 缓存value类型
     */
    private <K,V> CacheConfiguration<K, V> cacheConfiguration(Class<K> keyType, Class<V> valueType) {
        ResourcePools resourcePools = ResourcePoolsBuilder.newResourcePoolsBuilder()
                // 堆内存缓存
                .heap(10)
                // 堆内外存缓存
                .offheap(20, MemoryUnit.MB)
                // 磁盘缓存, 磁盘缓存需要持久化到磁盘
                .disk(30, MemoryUnit.MB, true)
                .build();
        return CacheConfigurationBuilder
                //缓存过期策略二选一
                .newCacheConfigurationBuilder(keyType, valueType, resourcePools)
                // 缓存过期策略,缓存中条目的最大空闲时间
                // .withExpiry(ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofSeconds(100)))
                // 缓存过期策略,缓存中条目的最大生命周期
                .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(500)))
                .build();
    }
}

五,配置常见错误

  1. 在Ehcache中,通常heap(堆内存)应该比offheap(堆外内存)小。因为offheap内存一般是用于存储从heap中溢出的缓存条目。如果heapoffheap大,Ehcache就无法正常工作。
  2. heap 的配置大小应该小于 disk 的配置大小。Ehcache 要求缓存的层次结构从小到大,即堆内存(heap)应该小于堆外内存(offheap),而堆外内存应小于磁盘缓存(disk)。
  3. 不能将CacheManager交给Spring管理,这样会导致在关闭时Spring会销毁两次,会导致报错,这个是Ehcache3出现的问题