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

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引入:

1<dependency> 2 <groupId>org.ehcache</groupId> 3 <artifactId>ehcache</artifactId> 4 <version>3.10.0</version> 5</dependency> 6<dependency> 7 <groupId>javax.cache</groupId> 8 <artifactId>cache-api</artifactId> 9</dependency>

2、创建实例:

1 /*************************** 1.纯内存操作 *****************************/ 2 // 1.1 创建缓存 preConfigured 基于 内存 3 CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() 4 .withCache("preConfigured", 5 CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10))) 6 .build(true); 7 8 // 1.2 获取缓存实例 9 Cache<Long, String> preConfigured = cacheManager.getCache("preConfigured", Long.class, String.class); 10 11 /*************************** 2.新增实例 *****************************/ 12 // 2.1 创建新的实例 并获取实例 13 Cache<Long, String> myCache = cacheManager.createCache("myCache", 14 CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10))); 15 16 /*************************** 3.三层存储-持久化磁盘 *****************************/ 17 // 3.1 创建缓存 myData 基于 内存->堆外存储->本地磁盘 18 PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder() 19 .with(CacheManagerBuilder.persistence(new File("/home/export/App/conf/", "myData"))) 20 .withCache("threeTieredCache", 21 CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, 22 ResourcePoolsBuilder.newResourcePoolsBuilder() 23 .heap(10, EntryUnit.ENTRIES) 24 .offheap(1, MemoryUnit.MB) 25 .disk(20, MemoryUnit.MB, true) 26 ) 27 ).build(true); 28 29 // 3.2 获取存储实例 threeTieredCache 30 Cache<Long, String> threeTieredCache = persistentCacheManager.getCache("threeTieredCache", Long.class, String.class); 31 32 /*************************** 4.一个manager管理多个缓存 - 持久化磁盘 *****************************/ 33 // 4.1 一个实例管理多个缓存 并且每个缓存都可持久化到本地磁盘 34 PersistentCacheManager persistentCacheManager1 = CacheManagerBuilder.newCacheManagerBuilder() 35 .with(CacheManagerBuilder.persistence( 36 new File("/path/to/persistent/directory1").getAbsoluteFile())) 37 .withCache("cache1", 38 CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, 39 ResourcePoolsBuilder.newResourcePoolsBuilder() 40 .heap(10, EntryUnit.ENTRIES) 41 .offheap(1, MemoryUnit.MB) 42 .disk(20, MemoryUnit.MB, true) 43 ) 44 ) 45 .with(CacheManagerBuilder.persistence( 46 new File("/path/to/persistent/directory2").getAbsoluteFile())) 47 .withCache("cache2", 48 CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, Integer.class, 49 ResourcePoolsBuilder.newResourcePoolsBuilder() 50 .heap(20, EntryUnit.ENTRIES) 51 .offheap(2, MemoryUnit.MB) 52 .disk(30, MemoryUnit.MB, true) 53 ) 54 ) 55 .build(true);

说明:

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

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

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

•堆内存 + 堆外存储

•堆内存 + 本地磁盘

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

3、用例

4、结果:

持久化磁盘:

5、结论:

缓存+磁盘测试OK;

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

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

👉其他说明:

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

6、以下为完成测试代码

1package com.jx.jxreserve.groupbuy.manager; 2 3import com.jd.flash.commons.exception.BaseBusinessException; 4import com.jx.jxreserve.groupbuy.common.enums.ErrorCode; 5import lombok.extern.slf4j.Slf4j; 6import org.ehcache.Cache; 7import org.ehcache.CacheManager; 8import org.ehcache.PersistentCacheManager; 9import org.ehcache.config.builders.CacheConfigurationBuilder; 10import org.ehcache.config.builders.CacheManagerBuilder; 11import org.ehcache.config.builders.ExpiryPolicyBuilder; 12import org.ehcache.config.builders.ResourcePoolsBuilder; 13import org.ehcache.config.units.EntryUnit; 14import org.ehcache.config.units.MemoryUnit; 15import org.springframework.stereotype.Component; 16 17import java.io.File; 18import java.time.Duration; 19 20import static org.ehcache.Status.AVAILABLE; 21 22/** 23 * @author zhangpengfei9 24 * @version V1.0 25 * Ehcache的测试管理类 26 */ 27@Slf4j 28@Component 29public class EhcacheTestManager { 30 31 /*************************** 1.纯内存操作 *****************************/ 32 // 1.1 创建缓存 preConfigured 基于 内存 33 CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() 34 .withCache("preConfigured", 35 CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10))) 36 .build(true); 37 38 // 1.2 获取缓存实例 39 Cache<Long, String> preConfigured = cacheManager.getCache("preConfigured", Long.class, String.class); 40 41 /*************************** 2.新增实例 *****************************/ 42 // 2.1 创建新的实例 并获取实例 43 Cache<Long, String> myCache = cacheManager.createCache("myCache", 44 CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10))); 45 46 /*************************** 3.三层存储 *****************************/ 47 // 3.1 创建缓存 myData 基于 内存->堆外存储->本地磁盘 48 PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder() 49 .with(CacheManagerBuilder.persistence(new File("/home/export/App/conf/", "groupData"))) 50 .withCache("testDiskCache", 51 CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, 52 ResourcePoolsBuilder.newResourcePoolsBuilder() 53 .heap(10, EntryUnit.ENTRIES) 54 .offheap(1, MemoryUnit.MB) 55 .disk(20, MemoryUnit.MB, true) 56 ) 57 .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(600))) // 设置缓存有效期 58 ).build(true); 59 60 61 /*************************** 4.多个缓存 - 三层存储 *****************************/ 62 // 4.1 一个实例管理多个缓存 并且每个缓存都可持久化到本地磁盘 63 PersistentCacheManager persistentCacheManager1 = CacheManagerBuilder.newCacheManagerBuilder() 64 .with(CacheManagerBuilder.persistence( 65 new File("/home/export/App/conf/").getAbsoluteFile())) 66 .withCache("cache1", 67 CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, 68 ResourcePoolsBuilder.newResourcePoolsBuilder() 69 .heap(10, EntryUnit.ENTRIES) 70 .offheap(1, MemoryUnit.MB) 71 .disk(20, MemoryUnit.MB, true) 72 ) 73 ) 74 .with(CacheManagerBuilder.persistence( 75 new File("/home/export/App/conf/").getAbsoluteFile())) 76 .withCache("cache2", 77 CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, Integer.class, 78 ResourcePoolsBuilder.newResourcePoolsBuilder() 79 .heap(20, EntryUnit.ENTRIES) 80 .offheap(2, MemoryUnit.MB) 81 .disk(30, MemoryUnit.MB, true) 82 ) 83 ) 84 .build(true); 85 86 /** 87 * 设置缓存 88 */ 89 public void setEhCache(Long key, String values) throws BaseBusinessException { 90 try { 91 // 获取存储实例 threeTieredCache 92 log.info("setEhCache.value:{},{}", values, key); 93 Cache<Long, String> testDiskCache = getManagerCache("testDiskCache"); 94 95 testDiskCache.put(key, values); 96 97 98 } catch (Exception e) { 99 log.error("setEhCache failure! Exception:", e); 100 throw new BaseBusinessException(ErrorCode.SYSTEM_DB_ERROR.getCode(), ErrorCode.SYSTEM_DB_ERROR.getMessage()); 101 } 102 } 103 104 /** 105 * 查询缓存 106 */ 107 public String getEhCache(Long key) throws BaseBusinessException { 108 try { 109 // 获取存储实例 threeTieredCache 110 log.info("getEhCache.key:{}", key); 111 Cache<Long, String> testDiskCache = getManagerCache("testDiskCache"); 112 return testDiskCache.get(key); 113 114 115 } catch (Exception e) { 116 log.error("setEhCache failure! Exception:", e); 117 throw new BaseBusinessException(ErrorCode.SYSTEM_DB_ERROR.getCode(), ErrorCode.SYSTEM_DB_ERROR.getMessage()); 118 } 119 } 120 121 /** 122 * 设置缓存 123 */ 124 public void closeEhCache() throws BaseBusinessException { 125 try { 126 // 获取存储实例 threeTieredCache 127 log.info("closeEhCache.persistentCacheManager.close1:{}", persistentCacheManager.getStatus()); 128 persistentCacheManager.close(); 129 log.info("closeEhCache.persistentCacheManager.close2:{}", persistentCacheManager.getStatus()); 130 } catch (Exception e) { 131 log.error("setEhCache failure! Exception:", e); 132 throw new BaseBusinessException(ErrorCode.SYSTEM_DB_ERROR.getCode(), ErrorCode.SYSTEM_DB_ERROR.getMessage()); 133 } 134 } 135 136 private Cache<Long, String> getManagerCache(String cache) { 137 // 3.1 创建缓存 myData 基于 内存->堆外存储->本地磁盘 138 log.info("persistentCacheManager.getStatus():{}", persistentCacheManager.getStatus()); 139 if (AVAILABLE != persistentCacheManager.getStatus()) { 140 persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder() 141 .with(CacheManagerBuilder.persistence(new File("/home/export/App/conf/", "groupData"))) 142 .withCache("testDiskCache", 143 CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, 144 ResourcePoolsBuilder.newResourcePoolsBuilder() 145 .heap(10, EntryUnit.ENTRIES) 146 .offheap(1, MemoryUnit.MB) 147 .disk(20, MemoryUnit.MB, true) 148 ) 149 ).build(true); 150 log.info("persistentCacheManager.getStatus1:{}", persistentCacheManager.getStatus()); 151 } 152 153 Cache<Long, String> testDiskCache = persistentCacheManager.getCache(cache, Long.class, String.class); 154 return testDiskCache; 155 } 156} 157 158

作者:京东零售 张鹏飞

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

点赞
收藏

评论区

加载中...

相关推荐

基于Spring Cache实现Caffeine、jimDB多级缓存实战

在早期参与涅槃氛围标签中台项目中,前台要求接口性能999要求50ms以下,通过设计Caffeine、ehcache堆外缓存、jimDB三级缓存,利用内存、堆外、jimDB缓存不同的特性提升接口性能,内存缓存采用Caffeine缓存,利用WTinyLFU算法获得更高的内存命中率;同时利用堆外缓存降低内存缓存大小,减少GC频率,同时也减少了网络IO带来的性能消耗;利用JimDB提升接口高可用、高并发;后期通过压测及性能调优999性能<20ms

J2Cache 和普通缓存框架有何不同,它解决了什么问题?

不少人看到J2Cache第一眼时,会认为这就是一个普普通通的缓存框架,和例如Ehcache、Caffeine、SpringCache之类的项目没什么区别,无非是造了一个新的轮子而已。事实上完全不是一回事!目前缓存的解决方案一般有两种:内存缓存(如Ehcache)——速度快,进程内可用集中式缓存(如Redis)——

J2Cache 没有 Redis 也可以实现多节点的缓存同步

J2Cache是一个两级的缓存框架,第一级是基于内存的数据缓存,支持caffeine、ehcache2和ehcache3,二级缓存只支持redis。在某些生产环境中你可能没有redis,但是又希望多个应用节点间的缓存数据是同步的。配置的方法很简单:1\.首先关闭二级缓存(使用none替代redis)j2cache

Java本地缓存框架系列

Caffeine是一个基于Java8的高性能本地缓存框架,其结构和GuavaCache基本一样,api也一样,基本上很容易就能替换。Caffeine实际上就是在GuavaCache的基础上,利用了一些Java8的新特性,提高了某些场景下的性能效率。这一章节我们会从Caffeine的使用引入,并提出一些问题,之后分析其源代码解

Redis 缓存性能实践及总结

一、前言在互联网应用中,缓存成为高并发架构的关键组件。这篇博客主要介绍缓存使用的典型场景、实操案例分析、Redis使用规范及常规Redis监控。二、常见缓存对比常见的缓存方案,有本地缓存,包括HashMap/ConcurrentHashMap、Ehcache、Memcache、GuavaCache等,缓存中间件包

Echache整合Spring缓存实例讲解

摘要:本文主要介绍了EhCache,并通过整合Spring给出了一个使用实例。一、EhCache介绍EhCache是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存,JavaEE和轻量级