看着文档学ehcache

简介:纯java实现的缓存组件(jar包),最主要的作用还是做应用内缓存(进程内),比如hibernate就默认使用ehcache做缓存。我们可以创建多个缓存(cache),缓存中以key-value的形式存储数据,数据结构较为单一。


存储位置:

heap:由GC管理的JVM堆

offheap:堆外内存(非JVM的堆)

disk:持久化到硬盘,cache.destroy()方法可以删除硬盘上的缓存


其他功能

Eviction Advisors

缓存策略,说白了就是控制哪些缓存数据可存储,哪些不可存储,比如可以只让key为偶数的存储,或者只有value为String类可以存储。我们可以定义一个实现了EvictionAdvisor接口的类来控制。


User Managed Caches

由用户自己管理的缓存,不由CacheManager管理,用户可以自己控制cache的生命周期等等。但是为了方便,一般都是由CacheManager来管理。


Transactions Support

支持事务,我觉得一般用不到,如果它仅仅是一个缓存,并且缓存的逻辑会包含在service方法里,而service方法由spring的事务来管理,那么没必要用到它。


Cache-through

包括read-through和write-through,把缓存作为SOR(system of record),我个人还没弄清楚作为系统级别的缓存数据有什么作用


ehcache与redis的区别

1.redis的数据结构比较丰富,有key-value、hash、set等;ehcache比较简单,只有key-value

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


编程方式配置cache

下面是两个例子

import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.junit.Test;

/**
 * writer: holien
 * Time: 2017-08-11 11:52
 * Intent: 入门测试ehcache
 */
public class EhCacheTest {
    @Test
    public void testEhcache() {
        // 创建缓存管理器和一个叫“preConfigured”的缓存实例
        CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
                .withCache("preConfigured",
                        CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class,
                                String.class, ResourcePoolsBuilder.heap(10)))
                .build();
        // 初始化缓存管理器
        cacheManager.init();
        Cache preConfigured = cacheManager.getCache("preConfigured", Long.class, String.class);
        preConfigured.put(1L, "zheng pens");
        // 覆盖上一个值
        preConfigured.put(1L, "pens");
        System.out.println(preConfigured.get(1L));
        System.out.println(preConfigured.containsKey(1L));
        // 不存在的key-value返回null
        System.out.println(preConfigured.get(2L));
        // 不允许值为null,报空指针错误
//      preConfigured.put(3L, null);

        // 不在创建cacheManeger时创建缓存实例,单独创建
        Cache cache2 = cacheManager.createCache("cache2",
                CacheConfigurationBuilder.newCacheConfigurationBuilder(
                        Integer.class, String.class,
                        ResourcePoolsBuilder.heap(5))
//                        .withEvictionAdvisor(new CustomEvictionAdvisor())
                        .build());

        cache2.put(100, "hello");
        System.out.println(cache2.get(100));
    }
}
这个例子中把缓存的数据存储到硬盘
import org.ehcache.PersistentUserManagedCache;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.config.builders.UserManagedCacheBuilder;
import org.ehcache.config.units.MemoryUnit;
import org.ehcache.core.spi.service.LocalPersistenceService;
import org.ehcache.impl.config.persistence.DefaultPersistenceConfiguration;
import org.ehcache.impl.config.persistence.UserManagedPersistenceContext;
import org.ehcache.impl.persistence.DefaultLocalPersistenceService;
import org.junit.Test;

import java.io.File;

/**
 * writer: holien
 * Time: 2017-08-11 22:40
 * Intent: 使用ehcache的持久化功能
 */
public class PersistenceCacheTest {
    @Test
    public void testPersistenceCache() throws Exception {
        LocalPersistenceService persistenceService = new DefaultLocalPersistenceService(
                new DefaultPersistenceConfiguration(new File("E:\\")));

        PersistentUserManagedCache<Long, String> cache = UserManagedCacheBuilder.newUserManagedCacheBuilder(Long.class, String.class)
                .with(new UserManagedPersistenceContext<Long, String>("persistentCache", persistenceService))
                .withResourcePools(ResourcePoolsBuilder.newResourcePoolsBuilder()
                        .disk(10L, MemoryUnit.MB, true))
                .build(true);
        // 把缓存只存进硬盘里,只要persistenceService相同,即使关闭,再次启动还是可以读取数据
        cache.put(42L, "The Answer!");
        System.out.println(cache.get(42L));
//        cache.remove(44L);

        // 手动关闭和销毁
        cache.close();
        // 删除硬盘上的缓存
//        cache.destroy();

        // 停止服务
        persistenceService.stop();
    }
}

创建CacheConfiguration时设置该缓存的entry的过期时间

.withExpiry(Expirations.timeToLiveExpiration(Duration.of(20, TimeUnit.SECONDS)))
官方实例的xml文件,各种属性作一个介绍

<ehcache:config
    xmlns:ehcache="http://www.ehcache.org/v3"
    xmlns:jcache="http://www.ehcache.org/v3/jsr107">

  <!--
     services to be managed and lifecycled by the CacheManager
     用来管理缓存管理器的生命周期,当使用UserCacheManager才需要用到
  -->
  <ehcache:service>
    <!--
      One element in another namespace, using our JSR-107 extension as an example here
      使用Jcache作为接口,即JSR-107扩展才需要用到
    -->
    <jcache:defaults>
      <jcache:cache name="invoices" template="myDefaultTemplate"/>
    </jcache:defaults>
  </ehcache:service>

  <!--
      必须拥有的属性,为cache起一个别名,给cacheManager管理
  -->
  <ehcache:cache alias="productCache">

    <!--
      key类型默认是Object类型
    -->
    <ehcache:key-type copier="org.ehcache.impl.copy.SerializingCopier">java.lang.Long</ehcache:key-type>

    <!--
      value类型默认是Object类型
    -->
    <ehcache:value-type copier="org.ehcache.impl.copy.SerializingCopier">com.pany.domain.Product</ehcache:value-type>

    <ehcache:expiry>
      <!--
             * <ttl>, time to live; 从创建entry到过期的时间
             * <class>, for a custom Expiry implementation; 自定义过期类
             * <none>, for no expiry 默认不过期
             下面为entry的空闲时间
      -->
      <ehcache:tti unit="minutes">2</ehcache:tti>
    </ehcache:expiry>

    <!--
        可选,advice,写一个实现了EvictionAdvisor接口的类来控制哪些entry被驱逐(比如key为偶数的才可以进行缓存)
    -->
    <ehcache:eviction-advisor>com.pany.ehcache.OddEvictionAdvisor</ehcache:eviction-advisor>

    <!--
      把cache当作系统缓存(系统记录)来使用
        Let's you configure your cache as a "cache-through",
        i.e. a Cache that uses a CacheLoaderWriter to load on misses, and write on mutative operations.
    -->
    <ehcache:loader-writer>
      <!--
        下面是自定义的一个类,我还没弄明白为什么要把缓存升级为系统缓存,这里留个疑问...
      -->
      <ehcache:class>com.pany.ehcache.integration.ProductCacheLoaderWriter</ehcache:class>
        <!-- Any further elements in another namespace -->
    </ehcache:loader-writer>

    <!--
        The maximal number of entries to be held in the Cache, prior to eviction starting
        此缓存最多可存储的键值对数
    -->
    <ehcache:heap unit="entries">200</ehcache:heap>
  </ehcache:cache>

  <!--
      缓存模板,可被其他缓存引用,避免重复定义相似的cache
  -->
  <ehcache:cache-template name="myDefaultTemplate">
    <ehcache:expiry>
      <ehcache:none/>
    </ehcache:expiry>
  </ehcache:cache-template>

  <!--
    引用上面定义的模板
  -->
  <ehcache:cache alias="customerCache" uses-template="myDefaultTemplate">
    <!--
      Adds the key and value type configuration
      在模板的基础上添加key、value的类型
    -->
    <ehcache:key-type>java.lang.Long</ehcache:key-type>
    <ehcache:value-type>com.pany.domain.Customer</ehcache:value-type>
    <!--
      Overwrites the capacity limit set by the template to a new value
      可以通过重写覆盖模板的属性
    -->
    <ehcache:heap unit="entries">200</ehcache:heap>
  </ehcache:cache>

</ehcache:config>

使用我自己定义的ehcache.xml配置的cache

<ehcache:config
        xmlns:ehcache="http://www.ehcache.org/v3"
        xmlns:jcache="http://www.ehcache.org/v3/jsr107">

    <ehcache:cache alias="test">
        <ehcache:key-type>java.lang.Long</ehcache:key-type>
        <ehcache:value-type>java.lang.String</ehcache:value-type>
        <ehcache:expiry>
            <ehcache:ttl>1000</ehcache:ttl>
        </ehcache:expiry>
        <ehcache:heap unit="entries">2</ehcache:heap>
    </ehcache:cache>

</ehcache:config>

import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.xml.XmlConfiguration;
import org.junit.Test;

import java.net.URL;


/**
 * writer: holien
 * Time: 2017-08-13 14:24
 * Intent: 使用xml配置cache
 */
public class XmlConfugureTest {
    @Test
    public void testXmlConfiguration() throws Exception {
//        URL location = new URL("file:E:\\IdeaProjects\\ehCacheTest\\web\\ehcache.xml");
        URL location = getClass().getResource("/ehcache.xml");
        System.out.println(location.toString());
        XmlConfiguration xmlConfiguration = new XmlConfiguration(location);
        CacheManager cacheManager = CacheManagerBuilder.newCacheManager(xmlConfiguration);
        cacheManager.init();
        Cache cache = cacheManager.getCache("test", Long.class, String.class);
        cache.put(1L, "111");
        cache.put(2L, "222");
        System.out.println(cache.get(1L));
        System.out.println(cache.get(2L));
    }
}

当然,最好还是由spring管理cacheManager,然后使用xml来配置cache的属性,这样管理起来比较方便,在另一篇文章,讲讲ehcache2与spring的整合以及注解。

cache运行期间添加监听器

ListenerObject listener = new ListenerObject(); 这个ListenerObject类就是我们自己定义的继承了CacheEventAdapter抽象类的类。

cache.getRuntimeConfiguration().registerCacheEventListener(listener, EventOrdering.ORDERED,    EventFiring.ASYNCHRONOUS, EnumSet.of(EventType.CREATED, EventType.REMOVED));

cache运行期间解除监听器

cache.getRuntimeConfiguration().deregisterCacheEventListener(listener); 

官方3.1v文档说提供了CacheEventAdapter抽象类方便我们重写监听器的方法,但是CacheEventAdapter 里的方法却是protected的,只能通过下载源码,然后改写源码重新打jar包,比较麻烦,后期修改也麻烦,也许是一个bug。


总结

ehcache也有集群的功能,但是我觉得ehcache还是适合一些简单的应用缓存,比如方法级别的,缓存方法的返回值。或者当作一个Map来存储不由GC管理的、可以持久化的数据,比如爬虫url的存储。


  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值