本地缓存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 获取存储实例 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
@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 {
            // 获取存储实例 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;
    }
}


作者:京东零售 张鹏飞

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/373178.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

打开双重el-dialog后出现遮罩后如何解决?

背景&#xff1a; 打开el-dialog后&#xff0c;再次打开另外一个el-dialog&#xff0c;出现以下画面。 解决方式&#xff1a;在第二个el-dialog增加append-to-body <el-dialog :close-on-click-modal“true” :visible.sync“createVisible” v-if“createVisible” :width…

【Java网络编程05】网络原理进阶(三)

1. HTTP协议概述 HTTP协议&#xff1a;又被称为"超文本传输协议"&#xff0c;是一种使用非常广泛的应用层协议&#xff0c;我们之前在文件章节介绍过文本文件与二进制文件的区别&#xff0c;文本可以看做字符串&#xff08;能在utf8/gbk等编码表中查找到合法字符&am…

【并发编程】原子累加器

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;并发编程 ⛺️稳重求进&#xff0c;晒太阳 JDK8之后有专门做累加的类&#xff0c;效率比自己做快数倍以上 累加器性能比较 参数是方法 // supplier 提供者 无中生有 ()->结果// func…

Springboot 整合 Quartz(定时任务框架)

一、java 定时任务调度的实现方式 1、Timer 特点是&#xff1a;简单易用&#xff0c;但由于所有任务都是由同一个线程来调度&#xff0c;因此所有任务都是串行执行的&#xff0c;同一时间只能有一个任务在执行&#xff0c;前一个任务的延迟或异常都将会影响到之后的任务&#…

SpringBoot 集成 WebSocket,实现后台向前端推送信息

SpringBoot 集成 WebSocket&#xff0c;实现后台向前端推送信息 在一次项目开发中&#xff0c;使用到了Netty网络应用框架&#xff0c;以及MQTT进行消息数据的收发&#xff0c;这其中需要后台来将获取到 的消息主动推送给前端&#xff0c;于是就使用到了MQTT&#xff0c;特此…

spring-authorization-server 公共客户端方式获取授权码和Token的流程

spring-authorization-serve【版本1.2.1】官方文档中提及了关于RegisteredClient中所涉及的客户端身份验证方法&#xff0c;也就是RegisteredClient中提及的clientAuthenticationMethods属性对应的“none”值&#xff0c;目前clientAuthenticationMethods属性支持的值包含&…

SpringBoot 登录检验JWT令牌 生成与校验

JWT官网 https://jwt.io/ 引入依赖 <dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version> </dependency>设置过期时间 LocalDateTime localDateTime LocalDateTime.now().…

《低功耗方法学》翻译——附录B:UPF命令语法

附录B&#xff1a;UPF命令语法 本章介绍了文本中引用的所选UPF命令的语法。 节选自“统一电源格式&#xff08;UPF&#xff09;标准&#xff0c;1.0版”&#xff0c;经该Accellera许可复制。版权所有&#xff1a;(c)2006-2007。Accellera不声明或代表摘录材料的准确性或内容&…

【经典项目】Java小游戏 —— 会说话的汤姆猫

一、游戏回顾 【预期效果】 【玩法介绍】 1、 和它说话&#xff0c;它将用有趣的声音重复你的话。 2、打它的头&#xff0c;它会装成被打的样子&#xff0c;连续打还会晕倒&#xff1b;抚摸肚子&#xff0c;它会打呼噜&#xff1b;打肚子&#xff0c;它会装肚子疼&#xff1b…

WhisperFusion:与 AI 无缝语音对话(超低延迟),深入理解用户每句话背后的含义

演示视频里面&#xff0c;那老哥问它问题之后&#xff0c;后面更改问题&#xff0c;依然能很好的记录问题变化的过程并给出答案。 WhisperFusion 是基于 WhisperLive 和 WhisperSpeech 的强大工具&#xff0c;将声音转文字和文字理解融为一体&#xff0c;让你与AI机器人无缝语…

双非本科准备秋招(10.2)—— JVM3:垃圾收集器

垃圾收集器 分为七种&#xff0c;如下&#xff1a; 从功能的角度分为 1、串行&#xff1a;Serial、Serial Old 2、吞吐量优先&#xff1a;Parallel Scavenge、Parallel Old 3、响应时间优先&#xff1a;CMS 吞吐量优先VS响应时间优先 吞吐量运行用户代码时间/(运行用户代码…

开源软件全景解析:驱动技术创新与行业革新的力量

目录 什么是开源 开源的核心 开源软件的特点 为什么程序员应该拥抱开源 1.学习机会&#xff1a; 2.社区支持&#xff1a; 3.提高职业竞争力&#xff1a; 4.加速开发过程&#xff1a; 5.贡献和回馈&#xff1a; 开源软件的影响力 开源软件多元分析&#xff1a; 开源…

Java实现婚恋交友网站 JAVA+Vue+SpringBoot+MySQL

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 会员管理模块2.3 新闻管理模块2.4 相亲大会管理模块2.5 留言管理模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 会员信息表3.2.2 新闻表3.2.3 相亲大会表3.2.4 留言表 四、系统展示五、核心代码5.…

【Java EE初阶十】多线程进阶二(CAS等)

1. 关于CAS CAS: 全称Compare and swap&#xff0c;字面意思:”比较并交换“&#xff0c;且比较交换的是寄存器和内存&#xff1b; 一个 CAS 涉及到以下操作&#xff1a; 下面通过语法来进一步进项说明&#xff1a; 下面有一个内存M&#xff0c;和两个寄存器A,B; CAS(M,A,B)&am…

AQS简介、AQS实现原理、线程夺取锁失败 AQS队列的变化、线程被唤醒时 AQS队列的变化

AQS AQS简介AQS实现原理场景01-线程抢夺锁失败时&#xff0c;AQS队列的变化场景02-线程被唤醒时&#xff0c;AQS队列的变化 AQS简介 AQS(全称AbstractQueuedSynchronizer)即队列同步器。它是构建锁或者其他同步组件的基础框 架(如ReentrantLock、ReentrantReadWriteLock、Sema…

docker核心技术

一. 从系统架构谈起 传统分层架构 vs 微服务 微服务改造 分离微服务的方法建议: 审视并发现可以分离的业务逻辑业务逻辑,在对业务领域不是特别熟悉的时候,按照部门职能进行划分,例如账号、财务等寻找天生隔离的代码模块,可以借助于静态代码分析工具如果可以闭环的解决一…

STM32F4学习

F4系统架构 8个主控总线7个被控总线 主控总线 Cortex-M4内核 I总线Cortex-M4内核 D总线Cortex-M4内核 S总线DMA1存储器总线DMA2存储器总线DMA2外设总线以太网DMA总线USB OTG HS DMA总线 被控总线 内部FLASH ICode总线内部FLASH DCode总线主要内部SRAM1&#xff08;112KB&a…

二分查找------蓝桥杯

题目描述&#xff1a; 请实现无重复数字的升序数组的二分查找 给定一个元素升序的、无重复数字的整型数组 nums 和一个目标值 target&#xff0c;写一个函数搜索 nums 中的target&#xff0c;如果目标值存在返回下标 (下标从0 开始)&#xff0c;否则返回-1 数据范围: 0 < l…

中继DHCP配置实验

实验大纲 1.构建网络拓扑结构图 2.对路由器进行配置 3.对DHCP服务器进行配置 4.对交换机S1进行配置&#xff08;创建vlan&#xff09; 5.配置路由器&#xff0c;并分配逻辑接口 1.构建网络拓扑结构图 2.对路由器进行配置 Router>en Router#conf t Enter configuratio…

R语言学习case11:ggplot 置信区间(包含多子图)

ggplot Geometric objects How are these two plots similar? 两个图都包含相同的x变量、相同的y变量&#xff0c;并且描述相同的数据。但是这两个图并不相同。每个图使用不同的可视化对象来表示数据。在ggplot2语法中&#xff0c;我们说它们使用不同的geoms。 geom是绘图…