EhCache

2021-02-26  本文已影响0人  随风_d6a2

一 介绍

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

特性

集成

可以单独使用,一般在第三方库中被用到的比较多(如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 比较

二 Hello World

依赖

  <dependencies>
    <dependency>
        <groupId>net.sf.ehcache</groupId>
        <artifactId>ehcache</artifactId>
        <version>2.10.2</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
  </dependencies>

配置文件

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">

  <!-- 磁盘缓存位置 -->
  <diskStore path="java.io.tmpdir/ehcache"/>

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

  <!-- helloworld缓存 -->
  <cache name="HelloWorldCache"
         maxElementsInMemory="1000"
         eternal="false"
         timeToIdleSeconds="5"
         timeToLiveSeconds="5"
         overflowToDisk="false"
         memoryStoreEvictionPolicy="LRU"/>
</ehcache>

测试类

package com.zyc;

import org.junit.Test;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;

public class Test1 {

    @Test
    public void test1() {
        // 1\. 创建缓存管理器
        CacheManager cacheManager = CacheManager.create("./src/main/resources/ehcache.xml");

        // 2\. 获取缓存对象
        Cache cache = cacheManager.getCache("HelloWorldCache");

        // 3\. 创建元素
        Element element = new Element("key1", "value1");

        // 4\. 将元素添加到缓存
        cache.put(element);

        // 5\. 获取缓存
        Element value = cache.get("key1");
        System.out.println(value);
        System.out.println(value.getObjectValue());

        // 6\. 删除元素
        cache.remove("key1");

        Person p1 = new Person("小明",18,"杭州");
        Element pelement = new Element("xm", p1);
        cache.put(pelement);
        Element pelement2 = cache.get("xm");
        System.out.println(pelement2.getObjectValue());

        System.out.println(cache.getSize());

        // 7\. 刷新缓存
        cache.flush();

        // 8\. 关闭缓存管理器
        cacheManager.shutdown();

    }

}

三 配置文件说明

diskStore

defaultCache

默认的缓存

cache

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

编程方式配置

Cache cache = manager.getCache("mycache"); 
CacheConfiguration config = cache.getCacheConfiguration(); 
config.setTimeToIdleSeconds(60); 
config.setTimeToLiveSeconds(120); 
config.setmaxEntriesLocalHeap(10000); 
config.setmaxEntriesLocalDisk(1000000);

持久化配置

类必须实现序列化接口,不需要的属性用transientx修饰

这种是所有数据都放到磁盘里去了

  <!-- helloworld缓存 -->
  <cache name="HelloWorldCache"
         maxElementsInMemory="1" //设置成1,overflowToDisk设置成true,只要有一个缓存元素,就直接存到硬盘上去
         eternal="false"
         timeToIdleSeconds="50000"
         timeToLiveSeconds="50000"
         overflowToDisk="true"
         diskPersistent="true" //设置成true表示缓存虚拟机重启期数据 
         memoryStoreEvictionPolicy="LRU"/>

自己决定说明时候持久化
测试得出以下两个方法在配置持久化环境的情况下都会将内存中的数据放到磁盘上

        cache.flush();

        // 8\. 关闭缓存管理器
        cacheManager.shutdown()

自动持久化

想利用spring 的注解,不想手动shutdown ,因此web.xml 配置listener 监听,在销毁的时候进行shutdown,这里利用ehcache 的监听.

    <!-- ehcache 磁盘缓存 监控,持久化恢复 -->
    <listener>
        <listener-class>net.sf.ehcache.constructs.web.ShutdownListener</listener-class>
    </listener>

直接杀死线程,这肯定监听不到。
更多资料(未测试过):记一次,ehcache缓存到磁盘,再恢复的过程

spring持久化测试情况
通过注解调用发现,每次test结束后,都会自动持久化

    @Test
    public void testPersist(){
        System.out.println(ehcacheService.getDataFromDB("tt1"));
        System.out.println(ehcacheService.getDataFromDB("tt1"));
    }

    @Cacheable(value="HelloWorldCache", key="#key")
    @Override
    public String getDataFromDB(String key) {
        System.out.println("从数据库中获取数据...");
        return key + ":" + String.valueOf(Math.round(Math.random()*1000000));
    }

四 Spring整合

spring注解

Spring对缓存的支持类似于对事务的支持。
首先使用注解标记方法,相当于定义了切点,然后使用Aop技术在这个方法的调用前调用后获取方法的入参和返回值,进而实现了缓存的逻辑

@Cacheable

表明所修饰的方法是可以缓存的:当第一次调用这个方法时,它的结果会被缓存下来,在缓存的有效时间内,以后访问这个方法都直接返回缓存结果不再执行方法中的代码段

参数

// 将缓存保存到名称为UserCache中,键为"user:"字符串加上userId值,如 'user:1'
@Cacheable(value="UserCache", key="'user:' + #userId")    
public User findById(String userId) {    
    return (User) new User("1", "mengdee");           
}    

// 将缓存保存进UserCache中,并当参数userId的长度小于12时才保存进缓存,默认使用参数值及类型作为缓存的key
// 保存缓存需要指定key,value, value的数据类型,不指定key默认和参数名一样如:"1"
@Cacheable(value="UserCache", condition="#userId.length() < 12")    
public boolean isReserved(String userId) {    
    System.out.println("UserCache:"+userId);    
    return false;    
}

@CachePut

与@Cacheable不同,@CachePut不仅会缓存方法的结果还会执行方法的代码段。它支持的属性和用法都与@Cacheable一致。一个缓存后就不执行代码了,一个还要执行)

@CacheEvict

与@Cacheable功能相反,@CacheEvict表明所修饰的方法是用来删除失效无用的缓存数据。

参数

//清除掉UserCache中某个指定key的缓存    
@CacheEvict(value="UserCache",key="'user:' + #userId")    
public void removeUser(User user) {    
    System.out.println("UserCache"+user.getUserId());    
}    

//清除掉UserCache中全部的缓存    
@CacheEvict(value="UserCache", allEntries=true)    
public final void setReservedUsers(String[] reservedUsers) {    
   System.out.println("UserCache deleteall");    
}

代码测试

目录结构

image

pom

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.zyc</groupId>
  <artifactId>ehcache1</artifactId>
  <version>0.0.1-SNAPSHOT</version>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <junit.version>4.10</junit.version>
    <spring.version>4.2.3.RELEASE</spring.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>

    <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-test</artifactId>
         <version>${spring.version}</version>
         <scope>test</scope>
     </dependency>

    <!-- springframework -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <dependency>
        <groupId>net.sf.ehcache</groupId>
        <artifactId>ehcache</artifactId>
        <version>2.10.3</version>
    </dependency>

   </dependencies>

</project>

接口实现

package com.zyc;

import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

import com.zyc.service.EhcacheService;

public class EhcacheServiceTest extends Test2 {

    @Autowired
    private EhcacheService ehcacheService;

    /*
     *  有效时间是5秒,第一次和第二次获取的值是一样的,因第三次是5秒之后所以会获取新的值
     */
    @Test
    public void testTimestamp() throws InterruptedException{
        System.out.println("第一次调用:" + ehcacheService.getTimestamp("param"));
        Thread.sleep(2000);
        System.out.println("2秒之后调用:" + ehcacheService.getTimestamp("param"));
        Thread.sleep(4000);
        System.out.println("再过4秒之后调用:" + ehcacheService.getTimestamp("param"));
    }
//  执行结果
//  第一次调用:1562460396352
//  2秒之后调用:1562460396352
//  再过4秒之后调用:1562460402359

    @Test
    public void testCache(){
        String key = "zhangsan";
        String value = ehcacheService.getDataFromDB(key); // 从数据库中获取数据...
        ehcacheService.getDataFromDB(key);  // 从缓存中获取数据,所以不执行该方法体
        ehcacheService.removeDataAtDB(key); // 从数据库中删除数据
        ehcacheService.getDataFromDB(key);  // 从数据库中获取数据...(缓存数据删除了,所以要重新获取,执行方法体)
    }
//  第二次调用已经用到了缓存
//  从数据库中获取数据...
//  从数据库中删除数据
//  从数据库中获取数据...

    @Test
    public void testPut(){
        String key = "mengdee";
        ehcacheService.refreshData(key);  // 模拟从数据库中加载数据
        String data = ehcacheService.getDataFromDB(key);//这个调用不会执行
        System.out.println("data:" + data); // data:mengdee::103385

        ehcacheService.refreshData(key);  // 模拟从数据库中加载数据
        String data2 = ehcacheService.getDataFromDB(key);
        System.out.println("data2:" + data2);   // data2:mengdee::180538    
    }

    @Test
    public void testFindById(){
        ehcacheService.findById("1"); // 模拟从数据库中查询数据
        ehcacheService.findById("1");
    }

    @Test
    public void testIsReserved(){
        ehcacheService.isReserved("123");
        ehcacheService.isReserved("123");//会缓存
        ehcacheService.isReserved("1234567890123");
        ehcacheService.isReserved("1234567890123");//不会用到缓存
    }

    @Test
    public void testRemoveUser(){
        // 线添加到缓存
        ehcacheService.findById("1");

        // 再删除
        ehcacheService.removeUser("1");

        // 如果不存在会执行方法体
        ehcacheService.findById("1");
    }

    @Test
    public void testRemoveAllUser(){
        ehcacheService.findById("1");
        ehcacheService.findById("2");

        ehcacheService.removeAllUser();

        ehcacheService.findById("1");
        ehcacheService.findById("2");

//      模拟从数据库中查询数据
//      模拟从数据库中查询数据
//      UserCache delete all
//      模拟从数据库中查询数据
//      模拟从数据库中查询数据
    }

}

作者:guideEmotion
链接:https://www.jianshu.com/p/154c82073b07
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

上一篇下一篇

猜你喜欢

热点阅读