本地缓存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引入:
<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 获取存储实例 threeTieredCacheCache<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
@Component
public 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 {// 获取存储实例 threeTieredCachelog.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 {// 获取存储实例 threeTieredCachelog.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 {// 获取存储实例 threeTieredCachelog.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;}
}
作者:京东零售 张鹏飞
来源:京东云开发者社区 转载请注明来源
相关文章:

本地缓存Ehcache的应用实践 | 京东云技术团队
java本地缓存包含多个框架,其中常用的包括:Caffeine、Guava Cache和Ehcache, 其中Caffeine号称本地缓存之王,也是近年来被众多程序员推崇的缓存框架,同时也是SpringBoot内置的本地缓存实现。但是除了Caffeine之外&…...
linux一键换源
使用方法 - LinuxMirrors 使用方法 一键执行命令# 中国大陆(默认) 海外地区 bash <(curl -sSL https://linuxmirrors.cn/main.sh)-----------------------------------| ⡇ ⠄ ⣀⡀ ⡀⢀ ⡀⢀ ⡷⢾ ⠄ ⡀⣀ ⡀⣀ ⢀⡀ ⡀⣀ ⢀⣀ || ⠧⠤ ⠇ ⠇⠸ …...
Python Scapy库实现ARP扫描和ARP欺骗
ARP扫描:检测指定IP网段中哪些主机是在线的,并获取它们的MAC地址 from scapy.all import * import argparse import threading import time import logging # 解析CIDR格式的网段,并返回IP地址列表 # >接受一个CIDR格式的网段…...

Fink CDC数据同步(六)数据入湖Hudi
数据入湖Hudi Apache Hudi(简称:Hudi)使得您能在hadoop兼容的存储之上存储大量数据,同时它还提供两种原语,使得除了经典的批处理之外,还可以在数据湖上进行流处理。这两种原语分别是: Update/Delete记录:H…...
线程和进程的区别及基础线程创建
1 线程和进程的区别 资源分配和调度: 进程(火车)是操作系统进行资源分配和调度的最小单位。它有自己的独立资源空间,包括内存、文件句柄等。线程(车厢)是CPU调度的最小单位。一个进程可以包含多个线程&…...
如何使用postman进行接口调试
使用Postman进行接口调试 有些时候我们写代码的时候,会发现接口有报错,提示参数错误,我们为了更好的排查错误原因,可以在Postman上进行接口调试。将url,请求方式,参数,cookie都填写到Postman中…...
Leetcode 198 打家劫舍
题意理解: 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 给定一个代…...

相机图像质量研究(9)常见问题总结:光学结构对成像的影响--工厂镜头组装
系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结:光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结:光学结构对成…...
Linux内核与驱动面试经典“小”问题集锦(5)
接前一篇文章:Linux内核与驱动面试经典“小”问题集锦(4) 问题6 问:mutex_lock和mutex_lock_interruptible的区别是什么? 备注:此问题也是笔者近期参加蔚来面试时遇到的一个问题。 答: 尽管…...

基于51 单片机的交通灯系统 源码+仿真+ppt
主要内容: 1)南北方向的绿灯、东西方向的红灯同时亮40秒。 2)南北方向的绿灯灭、黄灯亮5秒,同时东西方向的红灯继续亮。 3)南北方向的黄灯灭、左转绿灯亮,持续20秒,同时东西方向的红灯继续…...

【蓝桥杯冲冲冲】[NOIP2017 提高组] 宝藏
蓝桥杯备赛 | 洛谷做题打卡day29 文章目录 蓝桥杯备赛 | 洛谷做题打卡day29[NOIP2017 提高组] 宝藏题目背景题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1样例 #2样例输入 #2样例输出 #2提示题解代码我的一些话[NOIP2017 提高组] 宝藏 题目背景 NOIP2017 D2T2 题目描…...

C#中实现串口通讯和网口通讯(使用SerialPort和Socket类)
仅作自己学习使用 1 准备部份 串口通讯需要两个调试软件commix和Virtual Serial Port Driver,分别用于监视串口和创造虚拟串口。网口通讯需要一个网口调试助手,网络上有很多资源,我在这里采用的是微软商店中的TCP/UDP网络调试助手࿰…...
LeetCode回溯算法的解题思路
回溯法概念 回溯法:一种通过探索所有可能的候选解来找出所有的解的算法。如果候选解被确认不是一个解(或者至少不是最后一个解),回溯算法会通过在上一步进行一些变化抛弃该解,即回溯并且再次尝试。 应用场景 回溯算…...

泰克示波器(TBS2000系列)数学运算功能使用
目录 1 数学运算菜单1.1 运算符选择1.2 信源选择1.3 数学运算结果 1 数学运算菜单 Math运算按钮,用于实现对两个通道的信号进行实时的“加、减、乘”运算,计算时信源1在前面,信源2在运算符的右边,设置时设置信源与运算符就行了。…...

数据结构与算法之美学习笔记:50 | 索引:如何在海量数据中快速查找某个数据?
目录 前言为什么需要索引?索引的需求定义构建索引常用的数据结构有哪些?总结引申 前言 本节课程思维导图: 在第 48 节中,我们讲了 MySQL 数据库索引的实现原理。MySQL 底层依赖的是 B 树这种数据结构。留言里有同学问我ÿ…...

Python(SQLite)executescript用法
SQLite 数据库模块的游标对象还包含了一个 executescript() 方法,这不是一个标准的 API 方法,这意味着在其他数据库 API 模块中可能没有这个方法。但是这个方法却很实用,它可以执行一段 SQL 脚本。 例如,如下程序使用 executescr…...

BUUCTF-Real-[ThinkPHP]IN SQL INJECTION
目录 漏洞描述 漏洞分析 漏洞复现 漏洞描述 漏洞发现时间: 2018-09-04 CVE 参考:CVE-2018-16385 最高严重级别:低风险 受影响的系统:ThinkPHP < 5.1.23 漏洞描述: ThinkPHP是一款快速、兼容、简单的轻量级国产P…...
python安装步骤
安装 Python 的步骤如下: 在 Python 官方网站(https://www.python.org)上下载 Python 安装程序。运行下载的安装程序。在安装程序中选择要安装的 Python 版本(通常选择最新版本),并选择安装目录。确保勾选…...

BlueLotus 下载安装使用
说明 蓝莲花平台BlueLotus,是清华大学曾经的蓝莲花战队搭建的平台,该平台用于接收xss返回数据。 正常执行反射型xss和存储型xss: 反射型在执行poc时,会直接在页面弹出执行注入的poc代码;存储型则是在将poc代码注入用…...
.[hudsonL@cock.li].mkp勒索病毒数据怎么处理|数据解密恢复
导言: 在当今数字化时代,勒索病毒已成为网络安全领域的一大威胁。其中一种新近出现的勒索病毒是由[hudsonLcock.li].mkp[hendersoncock.li].mkp[myersairmail.cc].mkp制作的,它以其高效的加密算法和勒索方式而备受关注。本文91数据恢复将介绍…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...

7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...

Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分: 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...