乐趣区

关于ehcache:大厂都在用EhCache它到底比Redis强在哪里

故事背景

随着硬件价格的走低,大家对硬件的依赖越来越高。甚至据说,代码不重要,不行就加机器呗。比方缓存的应用,通常有基于虚拟机内存、基于磁盘存储、基于中间件(Redis 内存)等形式,咱们都晓得,最适宜的才是最好的,但实际中,往往是动不动就间接上 Redis。

那么,Redis 肯定是最好的抉择吗?单不说对内存的要求,从效率和性能上来说,也未必是最优的。所以,不同的场景应用不同的缓存策略才是高手应该谋求的。

这篇文章就带大家意识除 Redis 之外的另一种缓存框架:EhCache。之所以要写写,也是因为我的项目中使用了此框架,同时又遇到点问题,于是决定深入研究一下。钻研之后,发现还真有点意思~

EhCache 简介

EhCache 是一个纯 Java 的过程内缓存框架,具备疾速、精干的特点。留神的这里的关键字 过程,基于过程的缓存直觉通知咱们效率必定要高一些,因为它间接在过程之内进行操作,但不同利用之间缓存的共享可能就会有问题。

EhCache 是 Hibernate 中默认的 CacheProvider,Spring Boot 也对其进行了反对,Spring 中提供的缓存形象也反对对 EhCache 缓存框架的绑定,而且反对基于注解的形式来应用。因而,EhCache 是一款被宽泛应用的基于 Java 的高速缓存框架,应用起来也十分不便。

EhCache 提供了多种缓存策略,次要分为内存和磁盘两级,是一款面向通用缓存、Java EE 和轻量级容器的缓存框架。

EhCache 的特点

简略说一下该框架的特点:

  • 简略、疾速,领有多种缓存策略;
  • 缓存数据有两级:内存和磁盘,无需放心容量问题;
  • 缓存数据会在虚拟机重启的过程中写入磁盘;
  • 能够通过 RMI、可插入 API 等形式进行分布式缓存;
  • 具备缓存和缓存管理器的侦听接口;
  • 反对多缓存管理器实例,以及一个实例的多个缓存区域,并提供 Hibernate 的缓存实现;

EhCache 能够独自应用,但通常会与 Mybatis、Shiro 等三方类库联合应用。自己我的项目中应用 EhCache 就是联合 Shiro 来应用的。

除了长处,EhCache 也还有一些毛病。比方,十分占用磁盘空间,这是因为 DiskCache 的算法简略,只是对元素间接追加存储。这样尽管能够提高效率,但在应用频繁的零碎中,磁盘很快会满。

另外就是 不能保障数据安全,当然忽然 kill 掉 Java 过程时,可能会产生抵触。EhCache 解决抵触的办法是重建 Cache,这对 Cache 数据须要放弃时可能会产生影响。Cache 只是简略的减速,不能保证数据的平安。

EhCache 与 Redis

EhCache 间接在 JVM 中进行缓存,速度快,效率高。与 Redis 相比,操作简略、易用、高效,尽管 EhCache 也提供有缓存共享的计划,但对分布式集群的反对不太好,缓存共享实现麻烦。

Redis 是通过 Socket 拜访到缓存服务,效率比 EhCache 低,比数据库要快很多,解决集群和分布式缓存不便,有成熟的计划。

所以,如果是单体利用,或对缓存拜访要求很高,可思考采纳 EhCache;如果是大型零碎,存在缓存共享、分布式部署、缓存内容很大时,则倡议采纳 Redis

EhCache 架构图

看一下 EhCache 的架构图,大略理解一下它由几局部组成。

Cache Replication 局部提供了缓存复制的机制,用于分布式环境。EhCache 最后是独立的本地缓存框架组件,在前期的倒退中,联合 Terracotta 服务阵列模型,能够反对分布式缓存集群,次要有 RMI、JGroups、JMS 和 Cache Server 等传播方式进行节点间通信。

In-process APIs 则提供了基于 JSR、JMX 等规范的反对,可能较好的兼容和移植,同时对各类对象有较欠缺的监控管理机制。

Network APIs 则对外提供了基于 RESTful API、JMS API、Cache Server 等形式的反对。

在应用过程中,须要关注的外围局部便是两头的 Core 局部了。它蕴含了外围的 API 和概念:

  • CacheManager:缓存管理器,能够通过单例或者多例的形式创立,也是 Ehcache 的入口类。
  • Cache:每个 CacheManager 能够治理多个 Cache,每个 Cache 能够采纳 hash 的形式治理多个 Element。所有 cache 都实现了 Ehcache 接口;
  • Element:单条缓存数据的组成单位,用于寄存真正缓存内容的。

三者的治理能够用下图示意:

缓存过期策略

在架构图中还能够看到 Memory Store LRU、Memory Store LFU、Memory Store FIFO 等内存存储算法。也就是当缓存占用空间靠近临界值时,会采纳下面的淘汰策略来清理掉一部分数据。

EhCache 提供了三种淘汰算法:

  • FIFO:First In First Out,先进先出。判断被存储的工夫,离目前最远的数据优先被淘汰。
  • LRU:Least Recently Used,最近起码应用。判断最近被应用的工夫,目前最远的数据优先被淘汰。
  • LFU:Least Frequently Used,最不常常应用。在一段时间内,数据被应用次数起码的,优先被淘汰。

Ehcache 采纳的是懒淘汰机制,每次往缓存放入数据时,都会存一个工夫,在读取时要和设置的工夫做 TTL 比拟来判断是否过期。

EhCache 实战解析

理解了下面的基础知识之后,来试验一下 EhCache 如何应用。其中 EhCache2.x 和 EhCache3.x 的应用差距较大。

这里采纳比拟新的 3.9.6 版本,不同的版本在 API 的应用上会有所差别。

基于 API 应用 EhCache

EhCache 提供了基于 API 和 xml 两种模式创立 CacheManger 和 Cache。先来看基于 API 的模式:

在 pom 文件中引入 EhCache 依赖:

<dependency>
      <groupId>org.ehcache</groupId>
      <artifactId>ehcache</artifactId>
      <version>3.9.6</version>
</dependency> 

创立并应用的代码如下:

public class EhCacheTest {

    @Test
    public void test() {
        // 1、先创立一个 CacheManagerBuilder;// 2、应用 CacheManagerBuilder 创立一个预配置(pre-configured)缓存:第一个参数为别名,第二个参数用来配置 Cache;// 3、build 办法构建并初始化;build 中 true 参数示意进行初始化。CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
                .withCache("preConfigured",
                        CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
                                ResourcePoolsBuilder.heap(100)).build())
                .build(true);

        // 取回在设定的 pre-configured,对于 key 和 value 值类型,要求是类型平安的,否则将抛出 ClassCastException 异样。Cache<Long, String> preConfigured = cacheManager.getCache("preConfigured", Long.class, String.class);

        System.out.println("从缓存中获取 key 为 preConfigured:" + preConfigured);

        // 依据需要,通过 CacheManager 创立出新的 Cache。实例化和残缺实例化的 Cache 将通过 CacheManager.getCache API 返回。Cache<Long, String> myCache = cacheManager.createCache("myCache",
                CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
                        ResourcePoolsBuilder.heap(100)).build());
        // 应用 put 办法存储数据
        myCache.put(1L, "da one!");
        // 应用 get 办法获取数据
        String value = myCache.get(1L);
        System.out.println("从缓存中获取 key 为 1L:" + value);
        // close 办法将开释 CacheManager 所治理的缓存资源
        cacheManager.close();}
}

上述代码基于 API 的模式演示了如何创立 CacheManager 及 Cache,并对 Cache 进行设置和获取。

基于 XML 应用 EhCache

依赖 Jar 包不变,在 src/main/resources/ 目录下创立配置文件 ehcache.xml。

<config
        xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
        xmlns='http://www.ehcache.org/v3'
        xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core.xsd">

    <cache alias="foo">
        <key-type>java.lang.String</key-type>
        <value-type>java.lang.String</value-type>
        <resources>
            <heap unit="entries">20</heap>
            <offheap unit="MB">10</offheap>
        </resources>
    </cache>

    <cache-template name="myDefaults">
        <key-type>java.lang.Long</key-type>
        <value-type>java.lang.String</value-type>
        <heap unit="entries">200</heap>
    </cache-template>

    <cache alias="bar" uses-template="myDefaults">
        <key-type>java.lang.Number</key-type>
    </cache>

    <cache alias="simpleCache" uses-template="myDefaults" />

</config>

3.x 版本与 2.x 版本有所区别,在 xml 配置文件上非常明显。2.x 中以 ehcache 元素为根节点,而 3.x 则以 config 为根节点。

在上述 xml 中蕴含三局部:

  • 一般缓存 cache-foo:别名为 foo 的缓存,缓存的 Key-Value 值类型均为 String。如果没有指定,默认就是 Object 类型。
  • 缓存模板 cache-template:实现一个配置形象,以便在将来能够进行扩大;
  • 基于缓存模板的 cache-bar:应用了 cache-template 模板 myDefaults,并且笼罩了 key-type 类型,myDefaults 的 key-type 是 Long 类型,笼罩后成了 Number 类型;

cache 中其余属性及元素:

  • name 为名称;
  • alias 为别名;
  • key-type 为 key 的类型;
  • value-type 为 value 的类型;
  • heap 指定堆中可创立的实体格局,其中 unit=”entries”,示意前面的 20 是实体;
  • offheap 示意在开始淘汰过期缓存项之前,能够调配多达 10M 的堆内存;
  • uses-template 示意应用模板的名称;

当然,也能够通过 persistence 元素来配置缓存的目录等。其余属性的应用,大家能够缓缓摸索。

基于 Spring Boot 应用 EhCache

后面曾经提到,Spring 对缓存进行了反对,Spring Boot 也对缓存进行了主动配置的反对。上面就基于 Spring Boot 来实现 EhCache 的集成以及应用案例演示。

在 Spring Boot 中引入对应的 starter:

<!-- ehcache 依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.9.6</version>
</dependency>

在 application.properties 中配置增加如下配置:

spring.cache.ehcache.config=ehcache.xml

在 Spring Boot 启动类上增加 @EnableCaching 注解:

@EnableCaching
@SpringBootApplication
@MapperScan("com.secbro.mapper")
public class SpringBootMainApplication {public static void main(String[] args) {SpringApplication.run(SpringBootMainApplication.class, args);
    }
}

创立一个用户缓存的实体类 Person:

@Data
public class Person {public Person(int id,String name){
        this.id = id;
        this.name = name;
    }

    private int id;

    private String name;
}

对应的 Service 办法实现:

public interface PersonService {Person getById(int id);
}

@Slf4j
@Service("personService")
public class PersonServiceImpl implements PersonService {@Cacheable(value = "personCache", key = "#id")
    @Override
    public Person getById(int id) {log.info("查问 id={}的用户", id);
        if (id == 1) {return new Person(1, "Tom");
        } else if (id == 2) {return new Person(2, "Jim");
        }
        return new Person(3, "Other");
    }
}

通过 Spring 提供 @Cacheable 注解指定了缓存的名称为 personCache,key 为 id。在办法内打印日志,如果调用到办法内,则会打印。

编写单元测试类:

@Slf4j
@SpringBootTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class PersonServiceTest {

    @Resource
    private PersonService personService;

    @org.junit.jupiter.api.Order(1)
    @Test
    void testCache() throws InterruptedException {log.info("第 1 次查问 id= 1 的数据");
        personService.getById(1);
        log.info("第 2 次查问 id= 1 的数据");
        personService.getById(1);
        Thread.sleep(3000);
    }
}

两次调用对应的办法,打印日志如下:

c.s.s.PersonServiceTest                  : 第 1 次查问 id= 1 的数据
c.s.s.i.PersonServiceImpl                : 查问 id= 1 的用户
c.s.s.PersonServiceTest                  : 第 2 次查问 id= 1 的数据

能够看到,第一进入办法内进行查问,第二次便走了缓存。

对于 Spring 提供的 cache 注解的应用还有很多应用办法和场景,这里就不再开展了。

小结

因为工作恰好用到该技术,就钻研并写成文章带大家领略了 EhCache 的基本知识、技术架构、应用场景、API 应用以及基于 Spring Boot 的集成。整体而言,算是入门级别的,大家能够在此基础上进一步学习扩大。至于 EhCache 对分布式的反对局部,本文并未波及,次要起因是应用起来并没那么好用,如果感兴趣的话可自行钻研。

博主简介:《SpringBoot 技术底细》技术图书作者,热爱钻研技术,写技术干货文章。

公众号:「程序新视界」,博主的公众号,欢送关注~

技术交换:请分割博主微信号:zhuan2quan

退出移动版