故事背景
随着硬件价格的走低,大家对硬件的依赖越来越高。甚至听说,代码不重要,不行就加机器呗。比如缓存的使用,通常有基于虚拟机内存、基于磁盘存储、基于中间件(Redis 内存)等方式,我们都知道,最适合的才是最好的,但实践中,往往是动不动就直接上 Redis。
那么,Redis 一定是最好的选择吗?单不说对内存的要求,从效率和性能上来说,也未必是最优的。所以,不同的场景使用不同的缓存策略才是高手应该追求的。
这篇文章就带大家认识除 Redis 之外的另一种缓存框架:EhCache。之所以要写写,也是因为项目中运用了此框架,同时又遇到点问题,于是决定深入研究一下。研究之后,发现还真有点意思~
EhCache 简介
EhCache 是一个纯 Java 的进程内缓存框架,具有快速、精干的特点。注意的这里的关键字进程,基于进程的缓存直觉告诉我们效率肯定要高一些,因为它直接在进程之内进行操作,但不同应用之间缓存的共享可能就会有问题。
EhCache 是 Hibernate 中默认的 CacheProvider,Spring Boot 也对其进行了支持,Spring 中提供的缓存抽象也支持对 EhCache 缓存框架的绑定,而且支持基于注解的方式来使用。因此,EhCache 是一款被广泛使用的基于 Java 的高速缓存框架,使用起来也非常方便。
EhCache 提供了多种缓存策略,主要分为内存和磁盘两级,是一款面向通用缓存、Java EE 和轻量级容器的缓存框架。
EhCache 的特点
简单说一下该框架的特点:
- 简单、快速,拥有多种缓存策略;
- 缓存数据有两级:内存和磁盘,无需担心容量问题;
- 缓存数据会在虚拟机重启的过程中写入磁盘;
- 可以通过 RMI、可插入 API 等方式进行分布式缓存;
- 具有缓存和缓存管理器的侦听接口;
- 支持多缓存管理器实例,以及一个实例的多个缓存区域,并提供 Hibernate 的缓存实现;
EhCache 可以单独使用,但通常会与 Mybatis、Shiro 等三方类库结合使用。本人项目中使用 EhCache 就是结合 Shiro 来使用的。
除了优点,EhCache 也还有一些缺点。比如,非常占用磁盘空间,这是因为 DiskCache 的算法简单,只是对元素直接追加存储。这样虽然可以提高效率,但在使用频繁的系统中,磁盘很快会满。
另外就是不能保证数据安全,当然突然 kill 掉 Java 进程时,可能会产生冲突。EhCache 解决冲突的方法是重建 Cache,这对 Cache 数据需要保持时可能会产生影响。Cache 只是简单的加速,不能保证数据的安全。
EhCache 与 Redis
EhCache 直接在 JVM 中进行缓存,速度快,效率高。与 Redis 相比,操作简单、易用、高效,虽然 EhCache 也提供有缓存共享的方案,但对分布式集群的支持不太好,缓存共享实现麻烦。
Redis 是通过 Socket 访问到缓存服务,效率比 EhCache 低,比数据库要快很多,处理集群和分布式缓存方便,有成熟的方案。
所以,如果是单体应用,或对缓存访问要求很高,可考虑采用 EhCache;如果是大型系统,存在缓存共享、分布式部署、缓存内容很大时,则建议采用 Redis。
EhCache 架构图
看一下 EhCache 的架构图,大概了解一下它由几部分组成。
Ehcache-architecture
Cache Replication 部分提供了缓存复制的机制,用于分布式环境。EhCache 最初是独立的本地缓存框架组件,在后期的发展中,结合 Terracotta 服务阵列模型,可以支持分布式缓存集群,主要有 RMI、JGroups、JMS 和 Cache Server 等传播方式进行节点间通信。
In-process APIs 则提供了基于 JSR、JMX 等标准的支持,能够较好的兼容和移植,同时对各类对象有较完善的监控管理机制。
Network APIs 则对外提供了基于 RESTful API、JMS API、Cache Server 等方式的支持。
在使用过程中,需要关注的核心部分便是中间的 Core 部分了。它包含了核心的 API 和概念:
- CacheManager:缓存管理器,可以通过单例或者多例的方式创建,也是 Ehcache 的入口类。
- Cache:每个 CacheManager 可以管理多个 Cache,每个 Cache 可以采用 hash 的方式管理多个 Element。所有 cache 都实现了 Ehcache 接口;
- Element:单条缓存数据的组成单位,用于存放真正缓存内容的。
三者的管理可以用下图表示:
CacheManager
缓存过期策略
在架构图中还可以看到 Memory Store LRU、Memory Store LFU、Memory Store FIFO 等内存存储算法。也就是当缓存占用空间接近临界值时,会采用上面的淘汰策略来清理掉一部分数据。
EhCache 提供了三种淘汰算法:
- FIFO:First In First Out,先进先出。判断被存储的时间,离目前最远的数据优先被淘汰。
- LRU:Least Recently Used,最近最少使用。判断最近被使用的时间,目前最远的数据优先被淘汰。
- LFU:Least Frequently Used,最不经常使用。在一段时间内,数据被使用次数最少的,优先被淘汰。
Ehcache 采用的是懒淘汰机制,每次往缓存放入数据时,都会存一个时间,在读取时要和设置的时间做 TTL 比较来判断是否过期。
EhCache 实战解析
了解了上面的基础知识之后,来实验一下 EhCache 如何使用。其中 EhCache2.x 和 EhCache3.x 的使用差距较大。
这里采用比较新的 3.9.6 版本,不同的版本在 API 的使用上会有所差异。
基于 API 使用 EhCache
EhCache 提供了基于 API 和 xml 两种形式创建 CacheManger 和 Cache。先来看基于 API 的形式:
在 pom 文件中引入 EhCache 依赖:
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.9.6</version>
</dependency>
创建并使用的代码如下:
上述代码基于 API 的形式演示了如何创建 CacheManager 及 Cache,并对 Cache 进行设置和获取。
基于 XML 使用 EhCache
依赖 Jar 包不变,在 src/main/resources / 目录下创建配置文件 ehcache.xml。
3.x 版本与 2.x 版本有所区别,在 xml 配置文件上非常明显。2.x 中以 ehcache 元素为根节点,而 3.x 则以 config 为根节点。
在上述 xml 中包含三部分:
- 普通缓存 cache-foo:别名为 foo 的缓存,缓存的 Key-Value 值类型均为 String。如果没有指定,默认就是 Object 类型。
- 缓存模板 cache-template:实现一个配置抽象,以便在未来可以进行扩展;
- 基于缓存模板的 cache-bar:使用了 cache-template 模板 myDefaults,并且覆盖了 key-type 类型,myDefaults 的 key-type 是 Long 类型,覆盖后成了 Number 类型;
cache 中其他属性及元素:
- name 为名称;
- alias 为别名;
- key-type 为 key 的类型;
- value-type 为 value 的类型;
- heap 指定堆中可创建的实体格式,其中 unit=“entries”,表示后面的 20 是实体;
- offheap 表示在开始淘汰过期缓存项之前,可以分配多达 10M 的堆内存;
- uses-template 表示使用模板的名称;
当然,也可以通过 persistence 元素来配置缓存的目录等。其他属性的使用,大家可以慢慢探索。
基于 Spring Boot 使用 EhCache
前面已经提到,Spring 对缓存进行了支持,Spring Boot 也对缓存进行了自动配置的支持。下面就基于 Spring Boot 来完成 EhCache 的集成以及使用案例演示。
在 Spring Boot 中引入对应的 starter:
在 application.properties 中配置添加如下配置:
在 Spring Boot 启动类上添加 @EnableCaching 注解:
创建一个用户缓存的实体类 Person:
对应的 Service 方法实现:
通过 Spring 提供 @Cacheable 注解指定了缓存的名称为 personCache,key 为 id。在方法内打印日志,如果调用到方法内,则会打印。
编写单元测试类:
两次调用对应的方法,打印日志如下:
可以看到,第一进入方法内进行查询,第二次便走了缓存。
关于 Spring 提供的 cache 注解的使用还有很多使用方法和场景,这里就不再展开了。
小结
因为工作恰好用到该技术,就钻研并写成文章带大家领略了 EhCache 的基本知识、技术架构、使用场景、API 使用以及基于 Spring Boot 的集成。整体而言,算是入门级别的,大家可以在此基础上进一步学习扩展。至于 EhCache 对分布式的支持部分,本文并未涉及,主要原因是使用起来并没那么好用,如果感兴趣的话可自行研究。
本文参与 腾讯云自媒体分享计划,分享自作者个人站点 / 博客。
原始发表:2021-12-15 ,如有侵权请联系 [email protected] 删除