一,Ehcache2介绍
Ehcache 2 是一个广泛使用的 Java 缓存库,用于提升应用程序的性能和响应速度。它支持多种缓存策略和配置选项,使得在Java应用中实现缓存变得更加容易。
关键特性:
- 缓存存储:
- 内存缓存:数据存储在内存中,提供快速访问。
- heap内存:该层受 Java 垃圾收集的约束,在JVM内存中开辟的内存
- offHeap内存:不受 Java 垃圾收集 (GC) 的影响,在JVM外开辟的内存
- 磁盘缓存:当内存缓存满时,数据可以被写入磁盘,以节省内存并持久化数据。
- 内存缓存:数据存储在内存中,提供快速访问。
- 缓存策略:
- 过期策略:支持基于时间的过期策略(如 TTL 和 TTI),确保缓存的数据在一定时间后过期。
- LRU(最近最少使用):当缓存达到最大容量时,最少使用的数据会被移除。
- 缓存持久化:
- 内存和磁盘持久化:可以将缓存内容持久化到磁盘,以便在应用重启后恢复数据。
- 分布式缓存:
- 集群支持:Ehcache 可以通过 Terracotta 的集群支持实现分布式缓存。
- 灵活的配置:
- XML配置:支持使用 XML 文件进行详细配置。
- 程序化配置:可以通过 Java 代码进行配置。
组件:
- CacheManager:缓存管理器,负责管理所有的缓存实例。应用程序通常只有一个
CacheManager
实例,用于创建和配置缓存。 - Cache:缓存对象,包含缓存数据和缓存策略的实现。每个
Cache
对象负责具体的数据存储和过期策略。 - CacheLoader:用于从持久存储中加载缓存数据的接口。可以实现自定义的加载逻辑。
- CacheWriter:用于将缓存数据写入持久存储的接口。
二,快速入门
-
添加依赖
<!--Ehcache2--> <dependency> <groupId>org.ehcache</groupId> <artifactId>ehcache</artifactId> <version>2.10.9</version> </dependency>
-
配置Ehcache
创建一个 Ehcache 配置文件
ehcache.xml
,放置在src/main/resources
目录下。<ehcache> <!-- 定义磁盘存储路径为 d:/temp --> <diskStore path="d:/temp"/> <!-- maxEntriesLocalHeap:堆内存中最多存储 10000 个缓存条目 eternal:缓存条目不是永久有效的,会根据TTL或TTI过期 timeToIdleSeconds: 缓存条目在 120 秒内未被访问将过期 timeToLiveSeconds:缓存条目在 120 秒后无论是否被访问都将过期 maxEntriesLocalDisk:磁盘中最多存储 10000000 个缓存条目 diskExpiryThreadIntervalSeconds: 磁盘清理线程的运行间隔时间为 120 秒 memoryStoreEvictionPolicy: 堆内存中的条目使用 LRU(最近最少使用)策略进行驱逐 --> <!-- 名为 "myCache" 的具体缓存配置 --> <cache name="myCache" maxEntriesLocalHeap="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" maxEntriesLocalDisk="10000000" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> <persistence strategy="localTempSwap"/> </cache> </ehcache>
-
配置Ehcache的bean
import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class EhcacheConfig { @Bean public CacheManager cacheManager() { //加载配置文件 return new CacheManager(getClass().getClassLoader().getResourceAsStream("ehcache.xml")); } @Bean public Cache myCache(CacheManager cacheManager) { //返回可手动操作的cache的bean,方便进行一些手动加入缓存和删除缓存 return cacheManager.getCache("myCache"); } }
-
注入并使用缓存
@Slf4j @SpringBootTest public class SpringBoot2AdminTests { @Autowired private Cache myCache; @Test public void testCache(){ Element element = new Element("cacheKye","cacheValue"); myCache.put(element); Element element1 = myCache.get("cacheKye"); log.info("element:{}",element1); } }
三,核心API介绍
Ehcache 由一个 CacheManager 组成,它管理以 Caches 形式表示的逻辑数据集,一个 Cache 对象包含 Elements,这些 Elements 本质上是 名称-值对
。你可以使用 Cache 对象来保存任何你想保存在内存中的数据。
缓存的物理实现可以是内存中的缓存或磁盘上的缓存。这些组件的逻辑表示主要通过以下类实现:
CacheManager
:缓存管理器Cache
:缓存Element
:元素
这些类构成了Ehcache API的核心。这些类提供的方法主要负责提供对缓存或内存数据存储的编程访问。
3.1 CacheManager缓存管理器
CacheManager 类用于管理缓存。缓存的创建、访问和删除由命名的 CacheManager 控制。
缓存管理器(CacheManager)支持两种创建模式:
- 单例模式(singleton)
- 实例模式(instance)
这两种类型可以在同一个JVM中共存。然而,同一个JVM中不允许存在多个同名的缓存管理器。使用 CacheManager()
构造函数创建的非单例模式缓存管理器可能会违反这一规则,从而导致 NullPointerException
。如果你的代码可能在同一个JVM中创建多个同名的缓存管理器,为了避免这种错误,建议使用静态的 CacheManager.create()
方法。该方法会执行以下操作:
- 如果指定名称(或默认名称)的缓存管理器已经存在,
CacheManager.create()
方法会返回该缓存管理器。 - 如果指定名称(或默认名称)的缓存管理器不存在,
CacheManager.create()
方法会创建一个新的缓存管理器。
对于单例模式(singleton),调用 CacheManager.create(...)
方法会:
- 返回具有配置名称的现有单例缓存管理器(如果它已经存在)。
- 如果指定的单例缓存管理器尚不存在,则根据传入的配置创建一个新的单例缓存管理器。
要从配置中工作,可以使用 CacheManager.newInstance(...)
方法,该方法解析传入的配置,以获取现有的指定名称的缓存管理器,或在缓存管理器不存在时创建它。
CacheManager 创建方法的行为如下:
CacheManager.newInstance(Configuration configuration)
– 创建一个新的缓存管理器,或返回配置中指定名称的现有缓存管理器。CacheManager.create()
– 创建一个具有默认配置的新单例缓存管理器,或返回现有的单例缓存管理器。这与CacheManager.getInstance()
相同。CacheManager.create(Configuration configuration)
– 使用传入的配置创建一个单例缓存管理器,或返回现有的单例缓存管理器。new CacheManager(Configuration configuration)
– 创建一个新的缓存管理器,或在配置中指定的缓存管理器已存在时抛出异常,或者当参数(配置)为空时抛出异常。
请注意,在实例模式(非单例模式)下,可以在同一个JVM中同时创建和使用多个缓存管理器,每个缓存管理器都需要自己的配置。
如果受管理的缓存使用磁盘存储,则每个缓存管理器配置中指定的磁盘存储路径应该是唯一的。这是因为在创建新的缓存管理器时,会检查是否有其他缓存管理器使用相同的磁盘存储路径。根据你的持久化策略,Ehcache 将自动解决磁盘存储路径冲突,或者通知你必须显式配置磁盘存储路径。
如果管理的缓存仅使用内存存储,则没有特殊考虑。
3.2 Cache缓存
缓存(Cache) 是一个线程安全的逻辑表示数据元素集合的结构,类似于许多缓存系统中的缓存区域。一旦获得了对缓存的引用(通过 CacheManager
),就可以执行逻辑操作。这些操作的具体实现则由存储(stores)负责。
缓存的实例化 可以通过配置文件或使用 Cache()
构造函数程序完成。某些缓存特性,例如自动资源控制(ARC)相关的大小调整和固定(pinning),必须通过配置进行设置。
缓存方法 可用于获取有关缓存的信息(例如,getCacheManager()
、isNodeBulkLoadEnabled()
和 isSearchable()
),或执行某些缓存范围的操作(例如,flush
、load
、initialize
和 dispose
)。
Cache
类提供的方法 还允许你操作缓存元素(例如,get
、set
、remove
和 replace
),以及获取有关这些元素的信息(例如,isExpired
和 isPinned
)。
3.3 Element元素
元素(Element) 是缓存中的一个原子条目。它包含一个键、一个值和一个访问记录。元素可以被放入缓存中,也可以从缓存中移除。根据缓存的设置,元素还可以过期并被缓存自动移除。
除了针对可序列化(Serializable)的对象外,还有针对对象(Object)的 API。不可序列化的对象只能存储在堆内存中。如果尝试将它们持久化,将会被丢弃,并记录一个 DEBUG 级别的日志消息,但不会抛出错误。
API 说明:
- 对象 API 的方法与序列化 API 相同,唯一的区别是
Element
类中的返回方法:getKeyValue()
和getObjectValue()
用于对象 API,取代了getKey()
和getValue()
。
四,对缓存的操作
4.1 创建CacheManager
所有 Ehcache API 的使用都从创建 CacheManager
开始。以下代码片段展示了几种不同的创建方式:
单例模式(Singleton)与实例模式(Instance)
-
单例模式(Singleton): 使用默认配置创建一个单例
CacheManager
,然后列出缓存。CacheManager.create(); String[] cacheNames = CacheManager.getInstance().getCacheNames();
-
实例模式(Instance): 使用默认配置创建一个
CacheManager
实例,然后列出缓存。CacheManager manager = CacheManager.newInstance(); String[] cacheNames = manager.getCacheNames();
-
不同配置的多个实例: 创建两个
CacheManager
,每个使用不同的配置文件,然后列出每个CacheManager
中的缓存。CacheManager manager1 = CacheManager.newInstance("src/config/ehcache1.xml"); CacheManager manager2 = CacheManager.newInstance("src/config/ehcache2.xml"); String[] cacheNamesForManager1 = manager1.getCacheNames(); String[] cacheNamesForManager2 = manager2.getCacheNames();
4.2 加载xml配置文件
在创建 CacheManager
时,可以根据我们提供的xml配置文件加载配置
-
使用类路径中的默认配置文件创建
CacheManager
:CacheManager manager = CacheManager.newInstance();
默认路径是:/src/resource/ehcache.xml
-
使用指定的配置文件创建
CacheManager
:CacheManager manager = CacheManager.newInstance("src/config/ehcache.xml");
-
从类路径中的配置资源创建
CacheManager
:URL url = getClass().getResource("/ehcache.xml"); CacheManager manager = CacheManager.newInstance(url);
-
从
InputStream
中的配置创建CacheManager
:InputStream fis = new FileInputStream(new File("src/config/ehcache.xml").getAbsolutePath()); try { CacheManager manager = CacheManager.newInstance(fis); } finally { fis.close(); }
4.3 以编程方式添加和删除Cache
4.3.1 添加缓存
-
使用默认配置添加缓存
你可以使用默认配置将新缓存添加到
CacheManager
中。以下代码将一个名为testCache
的缓存添加到singletonManager
中:CacheManager singletonManager = CacheManager.create(); singletonManager.addCache("testCache"); Cache test = singletonManager.getCache("testCache");
-
使用自定义配置创建并添加缓存
创建一个使用自定义配置的缓存,并将其添加到
CacheManager
中。注意,缓存在添加到CacheManager
之前不可用:CacheManager singletonManager = CacheManager.create(); Cache memoryOnlyCache = new Cache("testCache", 5000, false, false, 5, 2); singletonManager.addCache(memoryOnlyCache); Cache test = singletonManager.getCache("testCache");
-
使用
CacheConfiguration
创建并添加缓存通过指定缓存配置创建缓存,并将其添加到
CacheManager
:// 使用默认配置创建单例 CacheManager CacheManager manager = CacheManager.create(); // 创建指定配置的缓存 Cache testCache = new Cache( new CacheConfiguration("testCache", maxEntriesLocalHeap) .memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.LFU) .eternal(false) .timeToLiveSeconds(60) .timeToIdleSeconds(30) .diskExpiryThreadIntervalSeconds(0) .persistence(new PersistenceConfiguration().strategy(Strategy.LOCALTEMPSWAP)) ); manager.addCache(testCache);
4.3.2 删除缓存
使用 CacheManager
删除名为 sampleCache1
的缓存:
CacheManager singletonManager = CacheManager.create();
singletonManager.removeCache("sampleCache1");
4.4 缓存基本操作
以下示例中,manager
是指向一个包含名为 sampleCache1
的缓存的 CacheManager
对象。
4.4.1 获取缓存引用
获取名为 sampleCache1
的缓存,该缓存已在配置文件中预配置:
Cache cache = manager.getCache("sampleCache1");
4.4.2 将元素放入缓存
将一个元素放入缓存中:
Cache cache = manager.getCache("sampleCache1");
Element element = new Element("key1", "value1");
cache.put(element);
4.4.3 更新缓存中的元素
更新缓存中的一个元素。尽管使用了 cache.put()
方法,Ehcache 知道已有元素存在,因此将 put
操作视为更新,以便通知缓存监听器:
Cache cache = manager.getCache("sampleCache1");
cache.put(new Element("key1", "value1"));
// 更新 "key1" 的条目
cache.put(new Element("key1", "value2"));
4.4.4 从缓存中获取元素
-
从键为
key1
的元素中获取一个可序列化的值:Cache cache = manager.getCache("sampleCache1"); Element element = cache.get("key1"); Serializable value = element.getValue();
-
从键为
key1
的元素中获取一个不可序列化的值:Cache cache = manager.getCache("sampleCache1"); Element element = cache.get("key1"); Object value = element.getObjectValue();
4.4.5 从缓存中移除元素
移除键为 key1
的元素:
Cache cache = manager.getCache("sampleCache1");
cache.remove("key1");
4.4.6 获取缓存大小
-
获取当前缓存中的元素数量:
Cache cache = manager.getCache("sampleCache1"); int elementsInMemory = cache.getSize();
-
获取当前内存存储中的元素数量:
Cache cache = manager.getCache("sampleCache1"); long elementsInMemory = cache.getMemoryStoreSize();
-
获取当前磁盘存储中的元素数量:
Cache cache = manager.getCache("sampleCache1"); long elementsInMemory = cache.getDiskStoreSize();
4.5 关闭CacheManager
使用完 CacheManager
后应当将其关闭。虽然它有一个关闭钩子,但最好在代码中关闭一下。
-
关闭单例 CacheManager
CacheManager.getInstance().shutdown();
-
关闭 CacheManager 实例
manager.shutdown();
五,XML配置详解
5.1 XML配置介绍
默认情况下,Ehcache 会在 Java 类路径的顶层查找名为 ehcache.xml
的 ASCII 或 UTF8 编码的 XML 配置文件。你可以通过使用各种 CacheManager
构造函数来指定替代路径和文件名,
为了避免资源冲突,每个创建的 CacheManager
需要一个 XML 配置。例如,目录路径和监听端口需要唯一值。Ehcache 会尝试解决冲突,如果发现冲突,会发出警告,提醒用户为多个 CacheManager
使用不同的配置。
ehcache-failsafe.xml
如果调用 CacheManager
的默认构造函数或工厂方法,Ehcache 会在类路径顶层查找名为 ehcache.xml
的文件。如果找不到,它会查找类路径中的 ehcache-failsafe.xml
。ehcache-failsafe.xml
文件打包在 Ehcache JAR 中,应始终能够找到。
ehcache-failsafe.xml
提供了一个简单的默认配置,帮助用户在创建自己的 ehcache.xml
之前快速启动。
使用 ehcache-failsafe.xml
时,Ehcache 会发出警告,提醒用户设置适当的配置。元素和属性的含义在 ehcache.xml
部分中进行了说明。
示例配置:
<ehcache>
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxEntriesLocalHeap="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxEntriesLocalDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</defaultCache>
</ehcache>
defaultCache
配置应用于任何未显式配置的缓存。defaultCache
默认出现在 ehcache-failsafe.xml
文件中,也可以添加到任何 Ehcache 配置文件中。
虽然 defaultCache
配置不是必需的,但如果程序创建缓存时没有加载 defaultCache
,会产生错误。
5.2 存储层特点
Ehcache 提供三种存储层,每种层级有其特定的用途和特性:
- 内存存储(Memory store):
- 受 Java 垃圾回收(GC)影响。
- 接受所有数据,无论是否可序列化
- 线程安全,适合多个并发线程使用
- 最快的存储选项
- 堆外存储(Off-heap store):
- 大小仅受可用 RAM 限制。
- 不受 Java GC 影响。
- 只能存储序列化的数据。
- 提供内存存储的溢出容量,确保缓存能够存储更多数据。
- 磁盘存储(Disk store):
- 备份内存数据,并为其他存储层提供溢出容量。
- 只能存储序列化的数据。
- 适用于长期存储和大规模数据的持久化。
5.3 动态更改缓存配置
自 Ehcache 2.0 以来,某些缓存配置参数可以在运行时动态修改。这些包括:
-
过期设置
-
timeToLive
(生存时间):元素在缓存中存在的最大秒数,无论是否被访问。达到此限制后,元素过期,将不再从缓存中返回。默认值为 0,表示没有 TTL 驱逐(无限生命周期)。 -
timeToIdle
(闲置时间):元素在缓存中存在的最大秒数,而不被访问。达到此限制后,元素过期,将不再从缓存中返回。默认值为 0,表示没有 TTI 驱逐(无限生命周期)。 -
注意:当
eternal
属性设置为true
时,会覆盖timeToLive
和timeToIdle
,使元素不会过期。
-
-
本地大小属性
maxEntriesLocalHeap
:本地堆中的最大条目数。maxBytesLocalHeap
:本地堆中的最大字节数。maxEntriesLocalDisk
:本地磁盘中的最大条目数。maxBytesLocalDisk
:本地磁盘中的最大字节数。
-
内存存储驱逐策略
-
CacheEventListeners 可以动态添加和移除
举例:
public static void main(){
CacheManager cacheManage=new CacheManager();
// 创建Cache配置对象
CacheConfiguration cacheConfiguration = new CacheConfiguration();
// 配置缓存的名字为 "myCache"
cacheConfiguration.setName("myCache");
// 设置堆内存中最多存储 1000 个缓存条目
cacheConfiguration.setMaxEntriesLocalHeap(1000);
// 设置磁盘中最多存储 10000 个缓存条目
cacheConfiguration.setMaxEntriesLocalDisk(10000);
// 设置缓存条目在 60 秒内未被访问将过期
cacheConfiguration.setTimeToIdleSeconds(60);
// 设置缓存条目在 120 秒后无论是否被访问都将过期
cacheConfiguration.setTimeToLiveSeconds(120);
// 设置缓存对象的清除策略为 LFU(最少使用策略)
cacheConfiguration.setMemoryStoreEvictionPolicy("LFU");
// 设置磁盘清理线程的运行间隔时间为 120 秒
cacheConfiguration.setDiskExpiryThreadIntervalSeconds(120);
// 设置缓存条目不是永久有效的
cacheConfiguration.setEternal(false);
// 创建持久化配置对象
PersistenceConfiguration persistenceConfiguration = new PersistenceConfiguration();
// 设置持久化策略为 LOCALTEMPSWAP(本地临时交换)
persistenceConfiguration.setStrategy("LOCALTEMPSWAP");
// 将持久化配置应用到缓存配置中
cacheConfiguration.persistence(persistenceConfiguration);
//创建使用cacheConfiguration的缓存
Cache cache = new Cache(cacheConfiguration());
//让CacheManager添加这个缓存
cacheManager.addCache(cache);
//获得该缓存
Cache myCache=cacheManager.getCache("myCache");
}
禁用动态配置
-
API方式
Cache cache = manager.getCache("sampleCache"); cache.disableDynamicFeatures();
-
在
ehcache.xml
文件中,你可以通过将<ehcache>
元素的dynamicConfig
属性设置为false
来禁用动态配置:<ehcache dynamicConfig="false"> <!-- 配置内容 --> </ehcache>
5.4 缓存对象清除策略
<ehcache>
<defaultCache
memoryStoreEvictionPolicy="LRU">
</ehcache>
针对上述memoryStoreEvictionPolicy
配置有如下取值:
FIFO
(First In First Out):先进先出LFU
(Least Frequently Used):最少使用,使用次数最少的条目将被清理LRU
(Least Recenly Used):最近最少使用,最近一段时间内使用次数最少的条目将被清理
5.5 缓存持久化策略
<ehcache>
<diskStore path="java.io.tmpdir"/>
<defaultCache>
<persistence strategy="localTempSwap"/>
</defaultCache>
</ehcache>
针对persistence
里面的strategy
属性配置持久化策略有:
localTempSwap
:当内存中的条目已经满了的时候,将条目临时存放在磁盘上,一旦重启就会消失。localRestartable
:该策略只对企业版 Ehcache 有用,它可以将内存里面的条目持久化到硬盘上,重启之后再从硬盘上恢复到内存中。none
:不持久化缓存的条目。distributed
:该策略是用于分布式情况下的。
5.6 模板配置
给出ehcache.xml的模板配置,剩下的根据配置的注释进行复制黏贴进行修改即可
<ehcache dynamicConfig="false">
<!-- 定义磁盘存储路径为 ../temp -->
<diskStore path="../temp"/>
<!--
maxEntriesLocalHeap:堆内存中最多存储 10000 个缓存条目
eternal:缓存条目不是永久有效的,会根据TTL或TTI过期
timeToIdleSeconds: 缓存条目在 120 秒内未被访问将过期
timeToLiveSeconds:缓存条目在 120 秒后无论是否被访问都将过期
maxEntriesLocalDisk:磁盘中最多存储 10000000 个缓存条目
diskExpiryThreadIntervalSeconds: 磁盘清理线程的运行间隔时间为 120 秒
memoryStoreEvictionPolicy: 堆内存中的条目使用 LRU(最近最少使用)策略进行驱逐
-->
<!--配置默认缓存-->
<defaultCache
maxEntriesLocalHeap="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxEntriesLocalDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</defaultCache>
<!-- 名为 "localCache" 的具体缓存配置 -->
<cache name="localCache"
maxEntriesLocalHeap="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxEntriesLocalDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</cache>
</ehcache>