看着文档学ehcache

来源:互联网 发布:非洲人眼中的中国知乎 编辑:程序博客网 时间:2024/05/17 01:15

简介:纯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的存储。


原创粉丝点击