本文是基于Ehcache 3.7官方文档,总结的自己比较在意的内容。详细内容可以参看官方文档:Ehcache 3.7 Documentation Overview.

一、介绍

1、什么是Ehcache

Ehcache 是一个开源的、基于标准的,健壮、可靠、快速、简单、轻量级的java分布式缓存,支持与其他框架的集成,是Hibernate默认的CacheProvider。同时也实现了JSR107的规范,是Jcache的一种实现。
Ehcache 目前提供四层模型,支持四种级别:

  • heap
  • off-heap
  • disk
  • clustered

同时提供了java内配置和xml配置,提供线程池、事务、管理器等,并支持扩展和监听事件,提供了disk持久化以及clustered持久化。

2、依赖引入

Ehcache非常轻量级,唯一需要的依赖是slf4j。
maven使用:

<dependency><groupId>org.ehcache</groupId><artifactId>ehcache</artifactId><version>3.7.1</version>
</dependency>

如果与其他框架集成,请相应的引入对应的依赖。
如果需要使用事务,从3.1版本,应该就单独剥离出去了,所以需要引入:

<dependency><groupId>org.ehcache</groupId><artifactId>ehcache-transactions</artifactId><version>3.7.1</version>
</dependency>

3、分层架构

Ehcache3.7 支持堆、堆外、磁盘以及集群缓存;但是除了堆之外外的三种缓存,缓存的键值对必须支持序列化和反序列化。
我们在使用的时候,可以单独使用任意一个,比如:

CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, Integer.class,ResourcePoolsBuilder.newResourcePoolsBuilder().offheap(1, MemoryUnit.GB));
  • heap:堆缓存,受到jvm的管控,可以不序列化,速度最快;默认使用的是引用传递,也可以使用复制器来进行值传递。可以设置缓存条数或者缓存的大小来进行堆缓存大小的设置。尽量不要设置的过大,否则容易引起GC.(如果设置缓存的大小,则计算比较麻烦,同时又多了一个计算缓存大小的过程)。
  • off-heap:堆外缓存,不受jvm的管控,受到RAM的限制,在ehcache中配置,至少1M。通过-XX:MaxDirectMemorySize限制ehcache可分配的最大堆外内存,但是实际使用发现,不配置也能使用。如果不配置,使用堆外缓存时,ehcache将会使用jvm的内存,最大值为堆内存,但实际比-Xmx要小,可以通过Runtime,getRuntime().maxMemory()获取。因此,对于堆内对垃圾收集的影响过于严重的大量数据,应该选择堆外。
  • disk:磁盘存储,尽量使用高性能的SSD。这一层的存储,不能在不同的CacheManager之间共享!
  • clustered:集群

以上只有 heap 支持 运行时改变大小!

CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build(true);
Cache<Long, String> cache = cacheManager.createCache("runforupdate", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(2)).build());
//do something
//change the heap
cache.getRuntimeConfiguration().updateResourcePools(ResourcePoolsBuilder.heap(10).build());

4、层组合

如果需要使用多个层,则需要遵循一定的规则。
规则:

  • 必须有heap层
  • disk和clusterd不能同时存在
  • 层的大小应该采用金字塔的方式,即金字塔上的层比下的层使用更少的内存

根据规则进行以下有效配置:

  • heap + offheap
  • heap + offheap + disk
  • heap + offheap + clustered
  • heap + disk
  • heap + clustered

对于多层,put、get的顺序:

  • 当将一个值放入缓存时,它直接最低层。比如heap + offheap + disk,直接会存储在disk层。
  • 当获取一个值,从最高层获取,如果没有继续向下一层获取,一旦获取到,会向上层推送,同时上层存储该值。

5、访问模式

Ehcache支持以下几种模式:

  • Cache-aside
  • Cache-as-SoR
    • Read-through
    • Write-through
    • Write-behind

六、缓存配置

配置方式有两种:

  • java配置
  • XML配置

二、使用

1、缓存过期和淘汰策略

(1) 过期策略

在缓存级别配置,有三种策略:

  • no expiry : 永不过期
  • time-to-live :创建后一段时间过期
  • time-to-idle : 访问后一段时间过期

示例:

  • java
     CacheConfiguration<String, String> configuration = CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, String.class, ResourcePoolsBuilder.heap(10)).withExpiry(ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofSeconds(300))).build();
  • xml
<cache alias="test"><expiry><ttl unit="seconds">300</ttl> </expiry><heap>10</heap>
</cache>

支持实现ExpiryPolicy接口来自定义过期策略。

(2) 淘汰策略
  • FIFO
  • LRU 默认策略
  • LFU

2、自定义Serializer

除了heap层,可以存储对象;其他所有层都只能通过二进制形式表示,所以需要对对象进行序列化和反序列化。Ehcache提供了Serializer来,所以我们除了可以直接实现Serializable,也可以通过Ehcache的Serializer来自定义。这里使用Kryo(比java的Serializable更快更小)结合Ehcache提供的Serizlizer来进行序列化和反序列化。

Ehcache默认给基本的数据类型都提供了优化的序列化器,如下:

  • java.io.Serializable
  • java.lang.Long
  • java.lang.Integer
  • java.lang.Float
  • java.lang.Double
  • java.lang.Character
  • java.lang.String
  • byte[]
(1) Serializer接口
package org.ehcache.spi.serialization;import java.nio.ByteBuffer;/*** Defines the contract used to transform type instances to and from a serial form.* <p>* Implementations must be thread-safe.* <p>* When used within the default serialization provider, there is an additional requirement.* The implementations must define a constructor that takes in a {@code ClassLoader}.* The {@code ClassLoader} value may be {@code null}.  If not {@code null}, the class loader* instance provided should be used during deserialization to load classes needed by the deserialized objects.* <p>* The serialized object's class must be preserved; deserialization of the serial form of an object must* return an object of the same class. The following contract must always be true:* <p>* {@code object.getClass().equals( mySerializer.read(mySerializer.serialize(object)).getClass())}* @param <T> the type of the instances to serialize* @see SerializationProvider*/
public interface Serializer<T> {/*** Transforms the given instance into its serial form.* @param object the instance to serialize* @return the binary representation of the serial form* @throws SerializerException if serialization fails*/ByteBuffer serialize(T object) throws SerializerException;/*** Reconstructs an instance from the given serial form.* @param binary the binary representation of the serial form* @return the de-serialized instance* @throws SerializerException if reading the byte buffer fails* @throws ClassNotFoundException if the type to de-serialize to cannot be found*/T read(ByteBuffer binary) throws ClassNotFoundException, SerializerException;/*** Checks if the given instance and serial form {@link Object#equals(Object) represent} the same instance.* @param object the instance to check* @param binary the serial form to check* @return {@code true} if both parameters represent equal instances, {@code false} otherwise* @throws SerializerException if reading the byte buffer fails* @throws ClassNotFoundException if the type to de-serialize to cannot be found*/boolean equals(T object, ByteBuffer binary) throws ClassNotFoundException, SerializerException;}

该接口只有三个方法,但是需要注意以下几点:

  • A. 实现类必须提供一个带有ClassLoader参数的构造器,比如:
public MySerializer(ClassLoader classLoader) {}
  • B. 实现类必须线程安全
  • C. 被序列化对象的class必须被保存;被序列化对象的class与反序列化后的对象的class必须相等。(如果在构造CacheManager时没有指定classLoader,则使用ehcache的默认classLoader)

同时如果实现 java.io.Closeable 接口,当关闭缓存管理器时,将调用该序列化器的close方法。

(2) 通用序列化器

Ehcache不建议使用通用序列化器,建议将序列化器注册限制在具体的类中,可以根据实际来进行改变。
使用Kryo作为序列化/反序列化工具,

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;import org.objenesis.strategy.StdInstantiatorStrategy;import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;import de.javakaffee.kryoserializers.ArraysAsListSerializer;public class KryoUtil {private static ThreadLocal<Kryo> kryos = new ThreadLocal<Kryo>() {@Overrideprotected Kryo initialValue() {Kryo kryo = new Kryo();// 支持对象循环引用,默认 truekryo.setReferences(true);// 是否需要强制注册,默认 falsekryo.setRegistrationRequired(false);// 支持无参构造器等(推荐每个类都有自己的无参构造器,可以为private)((Kryo.DefaultInstantiatorStrategy) kryo.getInstantiatorStrategy()).setFallbackInstantiatorStrategy(new StdInstantiatorStrategy());// 支持Arrays.asList()kryo.register(Arrays.asList("").getClass(), new ArraysAsListSerializer());return kryo;}};private static ThreadLocal<Output> outs = ThreadLocal.withInitial(() -> new Output(1024, 1024 * 1024));private static ThreadLocal<Input> ins = ThreadLocal.withInitial(() -> {return new Input();});/*** * @return*/public static Kryo get() {return kryos.get();}/*** Serializer* * @return*/public static byte[] serializer(Object object) {try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); Output out = outs.get();) {out.setOutputStream(bos);kryos.get().writeClassAndObject(out, object);out.flush();return bos.toByteArray();} catch (IOException e) {e.printStackTrace();}return null;}/*** 序列化* * @return*/public static ByteBuffer serializerByteBuffer(Object object) {return ByteBuffer.wrap(serializer(object));}/*** 反序列化* * @return*/@SuppressWarnings("unchecked")public static <T> T deserializer(byte[] b) {try (Input in = ins.get();) {in.setBuffer(b, 0, b.length);return (T) KryoUtil.get().readClassAndObject(in);}}/*** 反序列化* * @return*/public static <T> T deserializerByteBuffer(ByteBuffer binary) {byte[] bytes = new byte[binary.capacity()];binary.get(bytes);return deserializer(bytes);}
}

序列化器:

import java.io.Closeable;
import java.io.IOException;
import java.nio.ByteBuffer;import org.ehcache.spi.serialization.Serializer;
import org.ehcache.spi.serialization.SerializerException;import com.wzy.map.util.serializer.KryoUtil;public class EhcacheSerializer<T> implements Serializer<T>, Closeable {public EhcacheSerializer() {}public EhcacheSerializer(ClassLoader loader) {}@Overridepublic ByteBuffer serialize(T object) throws SerializerException {return KryoUtil.serializerByteBuffer(object);}@Overridepublic T read(ByteBuffer binary) throws ClassNotFoundException, SerializerException {return KryoUtil.deserializerByteBuffer(binary);}@Overridepublic boolean equals(T object, ByteBuffer binary) throws ClassNotFoundException, SerializerException {return object.equals(read(binary));}@Overridepublic void close() throws IOException {System.out.println("close...");}}

3、自定义Copier

官网定义,Copier is the Ehcache abstraction solving this: it is specific to the on-heap store.
默认情况下,堆上存储,保存的是对象的引用,可以通过key和value定义对应的Copier来自定义是存储引用还是重新创建一个对象。

public interface Copier<T> {/*** Creates a copy of the instance passed in.* <p>* This method is invoked as a value is read from the cache.** @param obj the instance to copy* @return the copy of the {@code obj} instance*/T copyForRead(T obj);/*** Creates a copy of the instance passed in.* <p>* This method is invoked as a value is written to the cache.** @param obj the instance to copy* @return the copy of the {@code obj} instance*/T copyForWrite(T obj);
}

可以使用Kryo来进行复制。

4、Cache as SOR

Ehcache 支持Read Through、Write Through、Write Behind;在3.7中,提供了CacheLoaderWriter接口。
需要注意的是,实现类必须是线程安全的,同时write behind级别不支持重试失败的写操作,需要我们自己实现。

这里业务代码不再同时维护cache和sor,业务代码只操作cache,把cache当作sor;而cache再对真实的数据库等进行读写操作!
这里以Write Behind为例子来简单模拟一下场景:

import java.util.concurrent.ConcurrentMap;import org.ehcache.impl.internal.concurrent.ConcurrentHashMap;
import org.ehcache.spi.loaderwriter.CacheLoaderWriter;public class SampleLoaderWriter<K, V> implements CacheLoaderWriter<K, V> {// sorprivate ConcurrentMap<K, V> map;public SampleLoaderWriter() {map = new ConcurrentHashMap<K, V>();// do something}@Overridepublic V load(K key) throws Exception {return (V) map.get(key);}@Overridepublic void write(K key, V value) throws Exception {map.putIfAbsent(key, value);}@Overridepublic void delete(K key) throws Exception {map.remove(key);}}

配置CacheLoaderWriter:

CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build(true);Cache<Long, String> cache = cacheManager.createCache("cache",CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10).offhea).withLoaderWriter(new SampleLoaderWriter<Long, String>()).add(WriteBehindConfigurationBuilder.newBatchedWriteBehindConfiguration(10, TimeUnit.SECONDS, 3).queueSize(10).concurrencyLevel(1).enableCoalescing()).build());

业务代码只需要处理cache就可以!

5、监听器

Ehcache 提供 CacheEventListerner 来监听缓存事件。

/*** Definition of the contract for implementing listeners to receive {@link CacheEvent}s from a* {@link org.ehcache.Cache Cache}.** @param <K> the key type for the observed cache* @param <V> the value type for the observed cache*/
public interface CacheEventListener<K, V> {/*** Invoked on {@link org.ehcache.event.CacheEvent CacheEvent} firing.* <p>* This method is invoked according to the {@link EventOrdering}, {@link EventFiring} and* {@link EventType} requirements provided at listener registration time.* <p>* Any exception thrown from this listener will be swallowed and logged but will not prevent other listeners to run.** @param event the actual {@code CacheEvent}*/void onEvent(CacheEvent<? extends K, ? extends V> event);}

实现 onEvent() 方法即可。因为监听器是在缓存级别注册的,因此只接收已注册的缓存的事件。可以在运行时注册监听器。
注册示例:

CacheEventListenerConfigurationBuilder cacheEventListenerConfiguration = CacheEventListenerConfigurationBuilder.newEventListenerConfiguration(new ListenerObject(), EventType.CREATED, EventType.UPDATED,EventType.REMOVED).unordered().asynchronous();final CacheManager manager = CacheManagerBuilder.newCacheManagerBuilder().withCache("foo",CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, String.class, ResourcePoolsBuilder.heap(10)).add(cacheEventListenerConfiguration)).build(true);

6、Eviction Advisor

Ehcache 提供EvictionAdvisor 接口,当ehcache试图从缓存中删除数据时,将调用该接口以确定给定的数据是否该清除。

Ehcache3 入门相关推荐

  1. 命令行打印文件树列表: tree

    Linux & Mac 1.下载tree lib //mac brew install tree //centos yum install tree //ubuntu apt-get inst ...

  2. 命令行打印文件树列表: tree 1

    Linux & Mac 1.下载tree lib //mac brew install tree //centos yum install tree //ubuntu apt-get inst ...

  3. js textarea换行

    利用html换行符或. 查看全文 http://www.taodudu.cc/news/show-4947167.html 相关文章: textarea 标签内换行 [解决回车键出现乱码的问题]禁用E ...

  4. 【Springboot 入门培训 】#18 SpringBoot Cache 缓存实现

    目录 1 EhCache3 1 执行环境设置 2 Ehcache3 应用 3 Ehcache3 xml设置 2 Caffeine 3 Infinispan 1 SpringBoot嵌入式缓存 2 Ja ...

  5. 用Construct 2制作入门小游戏~

    今天在软导课上了解到了Construct 2这个神器,本零基础菜鸟决定尝试做一个简单的小游戏(实际上是入门的教程啊= = 首先呢,肯定是到官网下载软件啊,点击我下载~ 等安装完毕后我便按照新手教程开始 ...

  6. Docker入门六部曲——Swarm

    原文链接:http://www.dubby.cn/detail.html?id=8738 准备工作 安装Docker(版本最低1.13). 安装好Docker Compose,上一篇文章介绍过的. 安 ...

  7. Docker入门六部曲——Stack

    原文链接:http://www.dubby.cn/detail.html?id=8739 准备知识 安装Docker(版本最低1.13). 阅读完Docker入门六部曲--Swarm,并且完成其中介绍 ...

  8. Docker入门六部曲——服务

    原文链接:http://www.dubby.cn/detail.html?id=8735 准备 已经安装好Docker 1.13或者以上的版本. 安装好Docker Compose.如果你是用的是Do ...

  9. 【springboot】入门

    简介: springBoot是spring团队为了整合spring全家桶中的系列框架做研究出来的一个轻量级框架.随着spring4.0推出而推出,springBoot可以説是J2SEE的一站式解决方案 ...

最新文章

  1. python的@修饰符
  2. Java的知识点31——线程同步
  3. 对于WebAssembly编译出来的.wasm文件js如何调用
  4. 全生命周期管理,是趋势更是未来
  5. windows 批处理bat,设置定时关机
  6. 前端接收pdf文件_雷达接收机的噪声系统及灵敏度
  7. 非常适合新手的jq/zepto源码分析05
  8. thrift之TTransport层的堵塞的套接字I/O传输类TSocket
  9. MySQL(一)面试集合
  10. JAVA 反射机制 获得 private 变量
  11. java将jsp页面表格导出excel表格数据_JSP 导出Excel表格的实例
  12. ANSYS_APDL——实例002-模态分析
  13. 哈尔滨工业大学车万翔:自然语言处理新范式
  14. 前路钉板系统在重建胸腰段稳定性中应用 [已发表]
  15. mysql lpad_MySQL LPAD字符串填充函数使用简介说明
  16. fullPage.js使用
  17. 新媒体运营教程:如何用直播进行裂变+转化?
  18. IDEA 显示Cannot resolve plugin org.apache.maven.pluginsmaven-site-plugin3.3
  19. 屏幕录制和编辑神器ScreenFlow轻松上手
  20. 一套Python入门视频

热门文章

  1. iphone已经信任anyproxy证书,还是提示无效
  2. 数据分析——统计学理论和方法
  3. Camtasia Studio 2020mac免费注册激活下载安装教程(屏幕录制及视频编辑软件)
  4. 证书的主要功能与用途
  5. 睿量30W迷你氮化镓,小巧耐用易携带,支持更多充电协议
  6. 工业采集网关实现PLC数据采集
  7. iOS开发 导航栏渐变 图片下拉放大 导航栏移动 仿QQ 微博 知乎导航栏
  8. null 和空值““
  9. html 怎么做分屏效果,纯css实现水平方向分屏和垂直方向分屏
  10. sincerit 1173 采矿