Ehcache3 入门
本文是基于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 入门相关推荐
- 命令行打印文件树列表: tree
Linux & Mac 1.下载tree lib //mac brew install tree //centos yum install tree //ubuntu apt-get inst ...
- 命令行打印文件树列表: tree 1
Linux & Mac 1.下载tree lib //mac brew install tree //centos yum install tree //ubuntu apt-get inst ...
- js textarea换行
利用html换行符或. 查看全文 http://www.taodudu.cc/news/show-4947167.html 相关文章: textarea 标签内换行 [解决回车键出现乱码的问题]禁用E ...
- 【Springboot 入门培训 】#18 SpringBoot Cache 缓存实现
目录 1 EhCache3 1 执行环境设置 2 Ehcache3 应用 3 Ehcache3 xml设置 2 Caffeine 3 Infinispan 1 SpringBoot嵌入式缓存 2 Ja ...
- 用Construct 2制作入门小游戏~
今天在软导课上了解到了Construct 2这个神器,本零基础菜鸟决定尝试做一个简单的小游戏(实际上是入门的教程啊= = 首先呢,肯定是到官网下载软件啊,点击我下载~ 等安装完毕后我便按照新手教程开始 ...
- Docker入门六部曲——Swarm
原文链接:http://www.dubby.cn/detail.html?id=8738 准备工作 安装Docker(版本最低1.13). 安装好Docker Compose,上一篇文章介绍过的. 安 ...
- Docker入门六部曲——Stack
原文链接:http://www.dubby.cn/detail.html?id=8739 准备知识 安装Docker(版本最低1.13). 阅读完Docker入门六部曲--Swarm,并且完成其中介绍 ...
- Docker入门六部曲——服务
原文链接:http://www.dubby.cn/detail.html?id=8735 准备 已经安装好Docker 1.13或者以上的版本. 安装好Docker Compose.如果你是用的是Do ...
- 【springboot】入门
简介: springBoot是spring团队为了整合spring全家桶中的系列框架做研究出来的一个轻量级框架.随着spring4.0推出而推出,springBoot可以説是J2SEE的一站式解决方案 ...
最新文章
- python的@修饰符
- Java的知识点31——线程同步
- 对于WebAssembly编译出来的.wasm文件js如何调用
- 全生命周期管理,是趋势更是未来
- windows 批处理bat,设置定时关机
- 前端接收pdf文件_雷达接收机的噪声系统及灵敏度
- 非常适合新手的jq/zepto源码分析05
- thrift之TTransport层的堵塞的套接字I/O传输类TSocket
- MySQL(一)面试集合
- JAVA 反射机制 获得 private 变量
- java将jsp页面表格导出excel表格数据_JSP 导出Excel表格的实例
- ANSYS_APDL——实例002-模态分析
- 哈尔滨工业大学车万翔:自然语言处理新范式
- 前路钉板系统在重建胸腰段稳定性中应用 [已发表]
- mysql lpad_MySQL LPAD字符串填充函数使用简介说明
- fullPage.js使用
- 新媒体运营教程:如何用直播进行裂变+转化?
- IDEA 显示Cannot resolve plugin org.apache.maven.pluginsmaven-site-plugin3.3
- 屏幕录制和编辑神器ScreenFlow轻松上手
- 一套Python入门视频
热门文章
- iphone已经信任anyproxy证书,还是提示无效
- 数据分析——统计学理论和方法
- Camtasia Studio 2020mac免费注册激活下载安装教程(屏幕录制及视频编辑软件)
- 证书的主要功能与用途
- 睿量30W迷你氮化镓,小巧耐用易携带,支持更多充电协议
- 工业采集网关实现PLC数据采集
- iOS开发 导航栏渐变 图片下拉放大 导航栏移动 仿QQ 微博 知乎导航栏
- null 和空值““
- html 怎么做分屏效果,纯css实现水平方向分屏和垂直方向分屏
- sincerit 1173 采矿