SpringCache整合Ehcache

Lou.Chen
大约 7 分钟

SpringCache 整合Ehcache

一 、介绍

​ EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认CacheProvider。Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。它具有内存磁盘存储,缓存加载器,缓存扩展,缓存异常处理程序,一个gzip缓存servlet过滤器,支持REST和SOAP api等特点。

特性
  • 快速、简单
  • 多种缓存策略
  • 缓存数据有两级:内存和磁盘,因此无需担心容量问题
  • 缓存数据会在虚拟机重启的过程中写入磁盘
  • 可以通过RMI、可插入API等方式进行分布式缓存
  • 具有缓存和缓存管理器的侦听接口
  • 支持缓存管理器实例,以及一个实例的多个缓存区域
  • 提供Hibernate的缓存实现
集成

​ 可以单独使用,一般在第三方库中被用到的比较多(如mybatis、shiro等)ehcache 对分布式支持不够好,多个节点不能同步,通常和redis一块使用

灵活性

​ ehcache具备对象api接口可序列化api接口不能序列化的对象可以使用出磁盘存储外ehcache的所有功能 支持基于Cache和基于Element的过期策略,每个Cache的存活时间都是可以设置和控制的。 提供了LRU、LFU和FIFO缓存淘汰算法,Ehcache 1.2引入了最少使用和先进先出缓存淘汰算法,构成了完整的缓存淘汰算法。 提供内存和磁盘存储,Ehcache和大多数缓存解决方案一样,提供高性能的内存和磁盘存储。 动态、运行时缓存配置,存活时间、空闲时间、内存和磁盘存放缓存的最大数目都是可以在运行时修改的。

应用持久化

​ 在vm重启后,持久化到磁盘的存储可以复原数据 Ehache是第一个引入缓存数据持久化存储的开源java缓存框架,缓存的数据可以在机器重启后从磁盘上重新获得 ​ 根据需要将缓存刷到磁盘。将缓存条目刷到磁盘的操作可以通过cache.fiush方法执行,这大大方便了ehcache的使用

二、ehcache 和 redis 比较

ehcache直接在jvm虚拟机中缓存,速度快,效率高;但是缓存共享麻烦,集群分布式应用不方便。

redis是通过socket访问到缓存服务,效率比ecache低,比数据库要快很多, 处理集群和分布式缓存方便,有成熟的方案。如果是单个应用或者对缓存访问要求很高的应用,用ehcache。如果是大型系统,存在缓存共享、分布式部署、缓存内容很大的,建议用redis。

三、基本配置

所有的存储操作还是SpringCache的那一套,只是存储采取的载体不同

1、pom.xml

ehcache

spring-boot-starter-cache

 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

        <!--ehcache依赖-->
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>2.10.6</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
2、yaml配置

若缓存配置文件名为ehcache.xml则无需配置此项

配置ehcache文件的存放位置 ==>

spring:
  cache:
    ehcache:
      config: classpath:ehcache/ehcache1.xml
3、ehcache配置文件

配置文件说明==>

diskStore

  • path :指定磁盘存储的位置

defaultCache

默认的缓存

cache

自定的缓存,当自定的配置不满足实际情况时可以通过自定义(可以包含多个cache节点

  • name : 缓存的名称,可以通过指定名称获取指定的某个Cache对象
  • maxElementsInMemory :内存中允许存储的最大的元素个数,0代表无限
  • clearOnFlush:内存数量最大时是否清除
  • eternal :设置缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期。根据存储数据的不同,例如一些静态不变的数据如省市区等可以设置为永不过时
  • timeToIdleSeconds : 设置对象在失效前允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大
  • timeToLiveSeconds :缓存数据的 生存时间(TTL),也就是一个元素从构建到消亡的最大时间间隔值,这只能在元素不是永久驻留时有效,如果该值是0就意味着元素可以停顿无穷长的时间。(和上面的两者取最小值)
  • overflowToDisk:内存不足时,是否启用磁盘缓存。
  • maxEntriesLocalDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
  • maxElementsOnDisk:硬盘最大缓存个数。
  • diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
  • diskPersistent:是否在VM重启时存储硬盘的缓存数据。默认值是false。
  • diskExpiryThreadIntervalSeconds磁盘失效线程运行时间间隔,默认是120秒。
  • memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。这里比较遗憾,Ehcache并没有提供一个用户定制策略的接口,仅仅支持三种指定策略,感觉做的不够理想。
<ehcache>
    <!-- 磁盘缓存位置 -->
    <!--
    diskStore:为缓存路径,ehcache分为内存和磁盘 2级,此属性定义磁盘的缓存位置
    user.home - 用户主目录
    user.dir - 用户当前工作目录
    java.io.tmpdir - 默认临时文件路径
-->
    <diskStore path="java.io.tmpdir/ehcache"/>

    <!--
       name:缓存名称。
       maxElementsInMemory:缓存最大数目
       maxElementsOnDisk:硬盘最大缓存个数。
       eternal:对象是否永久有效,一但设置了,timeout将不起作用。
       overflowToDisk:是否保存到磁盘,当系统宕机时
       timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
       timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
       diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
       diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
       diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
       memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
       clearOnFlush:内存数量最大时是否清除。
       memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
           FIFO,first in first out,这个是大家最熟的,先进先出。
           LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
           LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
-->

    <!-- 默认缓存 -->
    <defaultCache
            maxEntriesLocalHeap="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxEntriesLocalDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </defaultCache>

    <!-- 自定义缓存 -->
   	<!--name 为缓存名称-->
    <cache name="mycache"
           maxElementsInMemory="1000"
           eternal="false"
           timeToIdleSeconds="600"
           timeToLiveSeconds="600"
           overflowToDisk="true"
           memoryStoreEvictionPolicy="LRU"/>
</ehcache>

4、实体bean(实现serializable)
@Getter
@Setter
@ToString
public class User implements Serializable {
    private Integer id;
    private String username;
    private String address;
}
5、开启缓存
@SpringBootApplication
//启用缓存
@EnableCaching
public class JpaRestApplication {

    public static void main(String[] args) {
        SpringApplication.run(JpaRestApplication.class, args);
    }

}
6、service层
@Service
public class UserService {

    @Cacheable(cacheNames = "mycache",key = "#id")
    public User getUserById(Integer id){
        User user=new User();
        user.setId(id);
        System.out.println("id=====>"+id);
        return user;
    }

}

测试==>

@SpringBootTest
class JpaRestApplicationTests {

    @Autowired
    private UserService userService;
    @Test
    void contextLoads() {
        User userById = userService.getUserById(1);
        User userById1 = userService.getUserById(1);
        System.out.println(userById);
        System.out.println(userById1);
    }
}