写点什么

本地缓存 Ehcache 的应用实践 | 京东云技术团队

  • 2024-02-04
    北京
  • 本文字数:6925 字

    阅读完需:约 23 分钟

java 本地缓存包含多个框架,其中常用的包括:Caffeine、Guava Cache 和 Ehcache, 其中 Caffeine 号称本地缓存之王,也是近年来被众多程序员推崇的缓存框架,同时也是 SpringBoot 内置的本地缓存实现。但是除了 Caffeine 之外,还有一款也不错的本地缓存框架 Ehcache,具有快速、灵活,并支持内存和磁盘缓存,且提供了丰富的配置选项和缓存策略,接下来一起了解下 Ehcache。

一、Ehcache****是什么

Ehcache 是一个开源的 Java 本地缓存框架,它提供了快速、灵活的缓存解决方案。Ehcache 支持内存和磁盘缓存,并且可以与各种 Java 应用程序集成,包括基于 Spring 框架的应用程序。它提供了丰富的配置选项和缓存策略,可以帮助开发人员提高应用程序的性能和响应速度。Ehcache 还支持分布式缓存,可以与其他缓存系统集成,如 Terracotta 集群。

二、Ehcache 特点

1、 分层存储:


堆内存储: 利用 Java 的堆上 RAM 内存来存储缓存数据。该层使用与 Java 应用程序相同的堆内存,所有这些内存由 JVM 垃圾收集器扫描(GC)。JVM 使用的堆空间越多,垃圾收集暂停对应用程序性能的影响就越大。该存储速度非常快,但通常资源有限。


堆外存储: 大小受可用 RAM 的限制。不受 Java 垃圾收集 (GC) 的影响。速度相当快,但比堆上存储慢,因为在存储和重新访问数据时必须将数据移入和移出 JVM 堆。


磁盘存储: 利用磁盘(文件系统)来存储缓存数据。这种类型的存储资源通常非常丰富,但比基于 RAM 的存储慢一些。对于所有使用磁盘存储的应用程序,建议使用快速且专用的 SSD 磁盘来优化吞吐量。


集群存储(分布式): 此数据存储是远程服务器上的缓存。由于网络延迟以及建立客户端/服务器一致性等因素,集群存储会带来额外的性能损耗,性能上会更低一些。


2、 灵活有效期:


**没有期限:**缓存映射在应用存在下永远不会过期;


**生存周期:**缓存映射将在创建后的固定时间后过期;


**空闲时间:**缓存映射将在上次访问后的固定持续时间后过期;


**定制有效期:**通过重载 ExpiryPolicy 接口实现个性化的过期判断;


接口如下:



返回值定义:


Duration.ZERO: 表示立即过期


Duration.INFINITE: 映射永远不会过期


Duration.设置具体时间: 设置对应的时间后过期


Duration.设置时间周期: 设置对应的周期后过期


Duration 设置 null:之前的过期时间保持不变


3、淘汰策略


**LFU:**访问频率值小的缓存淘汰。


**LRU:**基于最近访问时间来进行淘汰。


**FIFO:**数据项最早进入缓存的时间来进行淘汰。

三、缓存原理

以三层为例:堆内存,堆外存储,本地磁盘存储。


架构图:



说明:cache-manager、cache、element 为 Ehcache 本地缓存的核心,通过数据写入的事务操作保证个层间的一致性。同时基于存储变更监听程序,针对变更的数据以及满足淘汰策略数据进行清理,亦或持久化至本地磁盘;


流程图(基于源码整理):


待补充

四、实际应用

1、pom 引入:


<dependency>      <groupId>org.ehcache</groupId>      <artifactId>ehcache</artifactId>      <version>3.10.0</version></dependency><dependency>      <groupId>javax.cache</groupId>      <artifactId>cache-api</artifactId></dependency>
复制代码


2、创建实例:


    /*************************** 1.纯内存操作 *****************************/    // 1.1 创建缓存 preConfigured 基于 内存    CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()            .withCache("preConfigured",                    CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10)))            .build(true);
// 1.2 获取缓存实例 Cache<Long, String> preConfigured = cacheManager.getCache("preConfigured", Long.class, String.class);
/*************************** 2.新增实例 *****************************/ // 2.1 创建新的实例 并获取实例 Cache<Long, String> myCache = cacheManager.createCache("myCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10)));
/*************************** 3.三层存储-持久化磁盘 *****************************/ // 3.1 创建缓存 myData 基于 内存->堆外存储->本地磁盘 PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder() .with(CacheManagerBuilder.persistence(new File("/home/export/App/conf/", "myData"))) .withCache("threeTieredCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .heap(10, EntryUnit.ENTRIES) .offheap(1, MemoryUnit.MB) .disk(20, MemoryUnit.MB, true) ) ).build(true);
// 3.2 获取存储实例 threeTieredCache Cache<Long, String> threeTieredCache = persistentCacheManager.getCache("threeTieredCache", Long.class, String.class);
/*************************** 4.一个manager管理多个缓存 - 持久化磁盘 *****************************/ // 4.1 一个实例管理多个缓存 并且每个缓存都可持久化到本地磁盘 PersistentCacheManager persistentCacheManager1 = CacheManagerBuilder.newCacheManagerBuilder() .with(CacheManagerBuilder.persistence( new File("/path/to/persistent/directory1").getAbsoluteFile())) .withCache("cache1", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .heap(10, EntryUnit.ENTRIES) .offheap(1, MemoryUnit.MB) .disk(20, MemoryUnit.MB, true) ) ) .with(CacheManagerBuilder.persistence( new File("/path/to/persistent/directory2").getAbsoluteFile())) .withCache("cache2", CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, Integer.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .heap(20, EntryUnit.ENTRIES) .offheap(2, MemoryUnit.MB) .disk(30, MemoryUnit.MB, true) ) ) .build(true);
复制代码


说明:


a. 上述常见缓存实例的方法有多个,其中第一种为纯内存操作,第三种为三层存储并持久化磁盘实例,下面以第三种方式进行测试验证;


日常应用可以进行组合使用,例如:


•堆内存 + 堆外存储 + 本地磁盘


•堆内存 + 堆外存储


•堆内存 + 本地磁盘


b. 如果选择本地磁盘存储(系统退出前需要使用 persistentCacheManager.close();释放资源,方可保证磁盘数据准确),后续系统重启后会加载磁盘内数据至缓存中,使得缓存在有效期内依然有效,可减少应用启动对后端 DB 的压力;


3、用例



4、结果:



持久化磁盘:




5、结论:


缓存+磁盘测试 OK;


有效期数据,失效后不返回;


系统重启加载磁盘数据正常;


👉其他说明:


Ehcache 可结合 Terracotta 插件实现分布式存储,对该部分感兴趣的同学可一起探讨。但是对于线上系统而言,若需分布式存储,建议直接使用 redis。Ehcache 的分布式实现并不可靠,核心还是采用广播集群方式,实现数据的同步及更新,并且性能受机器 IO,磁盘,网络等影响,在实际应用情况下,不会比 redis 更好。


6、以下为完成测试代码


package com.jx.jxreserve.groupbuy.manager;
import com.jd.flash.commons.exception.BaseBusinessException;import com.jx.jxreserve.groupbuy.common.enums.ErrorCode;import lombok.extern.slf4j.Slf4j;import org.ehcache.Cache;import org.ehcache.CacheManager;import org.ehcache.PersistentCacheManager;import org.ehcache.config.builders.CacheConfigurationBuilder;import org.ehcache.config.builders.CacheManagerBuilder;import org.ehcache.config.builders.ExpiryPolicyBuilder;import org.ehcache.config.builders.ResourcePoolsBuilder;import org.ehcache.config.units.EntryUnit;import org.ehcache.config.units.MemoryUnit;import org.springframework.stereotype.Component;
import java.io.File;import java.time.Duration;
import static org.ehcache.Status.AVAILABLE;
/** * @author zhangpengfei9 * @version V1.0 * Ehcache的测试管理类 */@Slf4j@Componentpublic class EhcacheTestManager {
/*************************** 1.纯内存操作 *****************************/ // 1.1 创建缓存 preConfigured 基于 内存 CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() .withCache("preConfigured", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10))) .build(true);
// 1.2 获取缓存实例 Cache<Long, String> preConfigured = cacheManager.getCache("preConfigured", Long.class, String.class);
/*************************** 2.新增实例 *****************************/ // 2.1 创建新的实例 并获取实例 Cache<Long, String> myCache = cacheManager.createCache("myCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10)));
/*************************** 3.三层存储 *****************************/ // 3.1 创建缓存 myData 基于 内存->堆外存储->本地磁盘 PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder() .with(CacheManagerBuilder.persistence(new File("/home/export/App/conf/", "groupData"))) .withCache("testDiskCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .heap(10, EntryUnit.ENTRIES) .offheap(1, MemoryUnit.MB) .disk(20, MemoryUnit.MB, true) ) .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(600))) // 设置缓存有效期 ).build(true);

/*************************** 4.多个缓存 - 三层存储 *****************************/ // 4.1 一个实例管理多个缓存 并且每个缓存都可持久化到本地磁盘 PersistentCacheManager persistentCacheManager1 = CacheManagerBuilder.newCacheManagerBuilder() .with(CacheManagerBuilder.persistence( new File("/home/export/App/conf/").getAbsoluteFile())) .withCache("cache1", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .heap(10, EntryUnit.ENTRIES) .offheap(1, MemoryUnit.MB) .disk(20, MemoryUnit.MB, true) ) ) .with(CacheManagerBuilder.persistence( new File("/home/export/App/conf/").getAbsoluteFile())) .withCache("cache2", CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, Integer.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .heap(20, EntryUnit.ENTRIES) .offheap(2, MemoryUnit.MB) .disk(30, MemoryUnit.MB, true) ) ) .build(true);
/** * 设置缓存 */ public void setEhCache(Long key, String values) throws BaseBusinessException { try { // 获取存储实例 threeTieredCache log.info("setEhCache.value:{},{}", values, key); Cache<Long, String> testDiskCache = getManagerCache("testDiskCache");
testDiskCache.put(key, values);

} catch (Exception e) { log.error("setEhCache failure! Exception:", e); throw new BaseBusinessException(ErrorCode.SYSTEM_DB_ERROR.getCode(), ErrorCode.SYSTEM_DB_ERROR.getMessage()); } }
/** * 查询缓存 */ public String getEhCache(Long key) throws BaseBusinessException { try { // 获取存储实例 threeTieredCache log.info("getEhCache.key:{}", key); Cache<Long, String> testDiskCache = getManagerCache("testDiskCache"); return testDiskCache.get(key);
} catch (Exception e) { log.error("setEhCache failure! Exception:", e); throw new BaseBusinessException(ErrorCode.SYSTEM_DB_ERROR.getCode(), ErrorCode.SYSTEM_DB_ERROR.getMessage()); } }
/** * 设置缓存 */ public void closeEhCache() throws BaseBusinessException { try { // 获取存储实例 threeTieredCache log.info("closeEhCache.persistentCacheManager.close1:{}", persistentCacheManager.getStatus()); persistentCacheManager.close(); log.info("closeEhCache.persistentCacheManager.close2:{}", persistentCacheManager.getStatus()); } catch (Exception e) { log.error("setEhCache failure! Exception:", e); throw new BaseBusinessException(ErrorCode.SYSTEM_DB_ERROR.getCode(), ErrorCode.SYSTEM_DB_ERROR.getMessage()); } }
private Cache<Long, String> getManagerCache(String cache) { // 3.1 创建缓存 myData 基于 内存->堆外存储->本地磁盘 log.info("persistentCacheManager.getStatus():{}", persistentCacheManager.getStatus()); if (AVAILABLE != persistentCacheManager.getStatus()) { persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder() .with(CacheManagerBuilder.persistence(new File("/home/export/App/conf/", "groupData"))) .withCache("testDiskCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .heap(10, EntryUnit.ENTRIES) .offheap(1, MemoryUnit.MB) .disk(20, MemoryUnit.MB, true) ) ).build(true); log.info("persistentCacheManager.getStatus1:{}", persistentCacheManager.getStatus()); }
Cache<Long, String> testDiskCache = persistentCacheManager.getCache(cache, Long.class, String.class); return testDiskCache; }}
复制代码


作者:京东零售 张鹏飞


来源:京东云开发者社区 转载请注明来源

用户头像

拥抱技术,与开发者携手创造未来! 2018-11-20 加入

我们将持续为人工智能、大数据、云计算、物联网等相关领域的开发者,提供技术干货、行业技术内容、技术落地实践等文章内容。京东云开发者社区官方网站【https://developer.jdcloud.com/】,欢迎大家来玩

评论

发布
暂无评论
本地缓存Ehcache的应用实践 | 京东云技术团队_京东科技开发者_InfoQ写作社区