freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

本地缓存Ehcache的应用实践 | 京东云技术团队
京东云技术团队 2024-02-04 11:00:07 277033

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;
    }
}


作者:京东零售 张鹏飞

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

# 企业安全 # 网络安全技术
本文为 京东云技术团队 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
京东云技术团队 LV.10
最懂产业的云
  • 1758 文章数
  • 91 关注者
质量视角下的系统稳定性保障--稳定性保障常态化自动化实践
2025-04-21
大促系统优化之应用启动速度优化实践
2025-04-21
循序渐进搭建复杂B端系统整洁架构
2025-04-21
文章目录