Detailed Explanation of Ehcache Caching Framework

1. Introduction to Ehcache

Ehcache is a widely used Java caching framework that can effectively improve application performance and reduce the number of interactions with the back-end database. It adopts a series of advanced caching strategies, including memory caching, disk caching, distributed caching, etc., and provides a wealth of APIs and tools to facilitate the reading, writing and management of caching.

Ehcache mainly has the following characteristics:

  • Fast: Ehcache adopts a series of efficient caching strategies to achieve fast data access and read and write, thereby improving application performance.
  • Scalable: Ehcache supports distributed caching, which can be easily extended to multiple servers, thereby improving the fault tolerance and throughput of the system.
  • High reliability: Ehcache has a variety of built-in caching strategies to support data persistence and recovery, and also provides a complete fault detection and correction mechanism to ensure the reliability and stability of cached data.
  • Ease of use: Ehcache provides a wealth of APIs and tools, which can easily complete the reading, writing and management of the cache. At the same time, it is also integrated with various frameworks and technologies, such as Spring, Hibernate, MyBatis, etc., so that users can use Ecache more conveniently.

2. Working principle

1. Cache write

When an application writes data to Ehcache, Ehcache will first check whether the data already exists in the cache. If the data already exists in the cache, the data is updated; otherwise, the data is written to the cache. The following is the detailed process of Ehcache cache writing.

  1. When an application requests to write a data item into Ehcache, the data item is passed to the Ehcache API.

  2. Ehcache first locates the corresponding Cache object according to the key-value pair of the data item.

  3. Ehcache according to the cache strategy in the configuration, such as whether a new element should be created in the cache, and whether the new element will eliminate the old element.

  4. If a new cache element needs to be created, Ehcache creates a new element and adds it to the Cache object.

  5. If the element already exists in the cache, Ehcache will update or replace the element according to the cache strategy.

  6. Ehcache returns the updated Cache object to the application.

2. Cache lookup

当应用程序需要查询缓存中的数据时,Ehcache 首先会检查该数据是否存在于缓存中。如果数据存在于缓存中,则直接返回缓存数据;否则,从数据库或其他资源获取数据,并将其存入缓存中。以下是 Ehcache 缓存查找的详细流程。

  1. 当应用程序请求从 Ehcache 中读取一个数据项时,这个请求被传递给Ehcache API。

  2. Ehcache首先根据该数据项的键值对定位其对应的Cache对象。

  3. Ehcache检查该数据项是否已经存在于缓存中。

  4. 如果数据项存在于缓存中,Ehcache可以直接将其返回给应用程序。

  5. 如果数据项不存在于缓存中,Ehcache就需要从数据库或其他数据源(如文件、网络等)中获取数据。

  6. 获取到数据后,Ehcache会将其添加到缓存中并返回给应用程序。

3. 缓存过期和驱逐

Ehcache 提供了多种缓存失效策略,例如基于时间的缓存失效、基于访问的缓存失效、基于大小的缓存失效等。当缓存数据过期或缓存空间不足时,Ehcache 会选择一部分缓存元素进行驱逐以腾出更多的内存空间。以下是 Ehcache 缓存过期和驱逐的详细流程。

  1. Ehcache会周期性地扫描缓存中的元素来标记那些已经过期的元素。

  2. Ehcache根据缓存策略(如基于时间、基于访问、基于大小等)判断哪些缓存元素应该被驱逐。

  3. 驱逐过程通常是异步执行的,Ehcache会在后台线程上执行这个操作。

4. 缓存持久化

Ehcache 还提供了缓存持久化功能,它可以将缓存中的数据持久化到磁盘或者其他数据源。在应用程序重启或者缓存失效后,Ehcache 可以从持久化存储中恢复数据,从而保证数据的完整性和可靠性。以下是 Ehcache 缓存持久化的详细流程。

  1. Ehcache使用磁盘存储或数据库等持久化技术来存储缓存数据。

  2. 当缓存中的数据更新时,Ehcache会自动将此数据持久化到持久化存储中。

  3. 在应用程序重启或者缓存失效后,Ehcache会从持久化存储中读取缓存数据并重新加载到内存中。

三、Ehcache 的基本使用

Spring Boot 中集成 Ehcache 缓存代码示例。

1. 添加 Ehcache 依赖

首先需要在项目的 pom.xml 文件中添加 Ehcache 缓存依赖,如下所示:

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

2. 配置 Ehcache

application.ymlapplication.properties 配置文件中添加 Ehcache 的配置信息,例如:

spring:
  ehcache:
    config: classpath:ehcache.xml

在上述配置中,我们指定了 Ehcache 的配置文件为 classpath:ehcache.xml,这样 Spring Boot 在启动时会自动加载该配置文件。

接下来,需要在 src/main/resources 目录下添加 ehcache.xml 配置文件,并编写相应的 Ehcache 配置信息。例如,下面是一个简单的 Ehcache 配置文件:

<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-3.0.xsd">

  <cache alias="myCache">
    <key-type>java.lang.String</key-type>
    <value-type>java.lang.String</value-type>
    <expiry>
      <ttu value="10m"/>
    </expiry>
    <heap>100</heap>
  </cache>

</config>

在上述配置中,我们定义了一个名为 myCache 的缓存,配置了键值类型、过期时间、最大存储数量等参数。

3. 使用 Ehcache

在完成 Ehcache 的配置后,就可以在 Spring Boot 中使用 Ehcache 缓存了。Spring Boot 提供了 @Cacheable@CachePut@CacheEvict 等注解来简化缓存操作,同时也支持基于缓存管理器的编程方式。

3.1 基于注解的缓存

首先介绍一下基于注解的缓存。Spring Boot 中,可以通过 @Cacheable@CachePut@CacheEvict 注解来实现对缓存的读写和删除操作。

3.1.1 @Cacheable 注解

@Cacheable 注解用于指定方法的返回值可被缓存,同时也可以指定缓存的 key 和 cacheNames,例如:

@Service
public class UserService {

    @Cacheable(cacheNames = "myCache", key = "#userId")
    public User getUserById(String userId) {
        System.out.println("get user by id: " + userId);
        return new User(userId, "张三");
    }

}

在上述代码中,我们使用 @Cacheable 注解将 getUserById() 方法返回值进行缓存,并指定了缓存的 key 为 userId,缓存名称为 myCache

3.1.2 @CachePut 注解

@CachePut 注解用于更新缓存中的数据,例如:

@Service
public class UserService {

    @CachePut(cacheNames = "myCache", key = "#user.id")
    public User updateUser(User user) {
        System.out.println("update user: " + user);
        return user;
    }

}

在上述代码中,我们使用 @CachePut 注解更新了缓存中键为 user.id 的缓存数据。

3.1.3 @CacheEvict 注解

@CacheEvict 注解用于清除缓存中的数据,例如:

@Service
public class UserService {

    @CacheEvict(cacheNames = "myCache", key = "#userId")
    public void deleteUserById(String userId) {
        System.out.println("delete user by id: " + userId);
    }

}

在上述代码中,我们使用 @CacheEvict 注解清除了缓存中键为 userId 的缓存数据。

3.2 基于缓存管理器的编程方式

除了注解方式外,还可以基于缓存管理器的编程方式来实现对缓存的读写和删除操作。Spring Boot 中,可以使用 CacheManagerCache 接口来实现缓存的管理和操作。

3.2.1 注入缓存管理器

使用基于缓存管理器的编程方式,首先需要注入使用的缓存管理器,例如:

@Service
public class UserService {

    @Autowired
    private CacheManager cacheManager;

}

在上述代码中,我们注入了 CacheManager 类型的 cacheManager 实例。

3.2.2 获取缓存实例

获取缓存实例的方式也很简单,可以通过缓存管理器的 getCache() 方法获取:

@Service
public class UserService {

    @Autowired
    private CacheManager cacheManager;

    public User getUserById(String userId) {
        Cache cache = cacheManager.getCache("myCache");
        Element element = cache.get(userId);
        if (element != null) {
            System.out.println("get user by id from cache: " + userId);
            return (User) element.getObjectValue();
        }
        System.out.println("get user by id from db: " + userId);
        User user = new User(userId, "张三");
        cache.put(new Element(userId, user));
        return user;
    }

}

在上述代码中,我们使用缓存管理器的 getCache() 方法获取名为 myCache 的缓存实例。然后,我们使用缓存实例的 get() 方法获取指定 key 对应的缓存元素,如果获取到了元素,则直接返回缓存数据;否则,从数据库中获取数据,并将其存入缓存中。

3.2.3 缓存操作

除了读取缓存数据外,我们还可以使用缓存实例的 put() 方法向缓存中添加数据,使用 remove() 方法删除缓存数据,例如:

@Service
public class UserService {

    @Autowired
    private CacheManager cacheManager;

    public User updateUser(User user) {
        Cache cache = cacheManager.getCache("myCache");
        cache.put(new Element(user.getId(), user));
        return user;
    }

    public void deleteUserById(String userId) {
        Cache cache = cacheManager.getCache("myCache");
        cache.remove(userId);
    }

}

在上述代码中,我们分别使用了缓存实例的 put()remove() 方法对缓存数据进行了更新和清除操作。

四、 Ehcache 的高级用法

1. 缓存策略

Ehcache 提供了多种缓存策略,可以根据实际需求选择合适的策略。其中,最常用的包括:

  • LRU(Least Recently Used):移除最近最少使用的缓存项。
  • LFU(Least Frequently Used):移除最不经常使用的缓存项。
  • FIFO(First In, First Out):先进先出,即移除最早加入的缓存项。
  • TTL(Time To Live):根据缓存项的过期时间来判断是否要移除该项。
  • 随机替换:随机选择一项进行移除。

在 Ehcache 中,默认使用的是 LRU 策略。如果需要更改策略,可以在 ehcache.xml 配置文件中进行设置。

2. 内存缓存和磁盘缓存

Ehcache 支持将缓存数据同时存储在内存和磁盘上,并根据实际情况调整数据的存储位置。例如,可以将经常访问的缓存数据存储在内存中,而不常使用的数据则存储在磁盘上,从而达到提高缓存效率和降低成本的目的。

ehcache.xml 配置文件中,可以通过 <heap><offheap><disk> 标签来定义缓存存储的位置。例如,下面的配置将缓存数据存储在内存中,并限制最大存储数量为 100 个:

<cache alias="myCache">
  <key-type>java.lang.String</key-type>
  <value-type>java.lang.String</value-type>
  <heap>100</heap>
</cache>

如果需要将部分缓存数据存储在磁盘上,可以添加如下配置:

<cache alias="myCache">
  <key-type>java.lang.String</key-type>
  <value-type>java.lang.String</value-type>
  <heap>10</heap> <!-- 最多存储 10 个缓存项到堆中 -->
  <offheap>1MB</offheap> <!-- 最多存储 1MB 缓存项到堆外内存中 -->
  <diskPersistent>true</diskPersistent> <!-- 开启磁盘持久化,即使程序重启后,缓存仍然有效 -->
  <diskSpoolBufferSizeMB>30</diskSpoolBufferSizeMB> <!-- 磁盘缓存区大小,缓存数据先以临时文件的形式写入磁盘缓存区,待达到一定阈值后再写入磁盘 -->
  <diskExpiryThreadIntervalSeconds>120</diskExpiryThreadIntervalSeconds> <!-- 磁盘数据过期检测间隔时间,单位为秒 -->
  <maxEntriesLocalDisk>1000</maxEntriesLocalDisk> <!-- 磁盘缓存最大存储数量 -->
</cache>

3. 分布式缓存

Ehcache 提供了分布式缓存的支持,并可以通过 RMI、JMS、Spring 等方式进行实现。在分布式环境下,每个缓存节点都可以通过网络访问远程节点上的缓存数据,从而实现数据共享和分布式缓存的目的。

ehcache.xml 配置文件中,可以添加 <cacheManagerPeerProviderFactory><cacheManagerPeerListenerFactory> 标签来定义缓存节点的配置信息。例如,下面的配置定义了一个名为 myCache 的缓存,采用了 RMI 方式实现分布式缓存:

<cache-manager-peer-provider-factory class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
                                      properties="peerDiscovery=automatic,
                                                  multicastGroupAddress=230.0.0.1,
                                                  multicastGroupPort=4446,
                                                  timeToLive=32"/>

<cache-manager-peer-listener-factory class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
                                      properties="port=40001,
                                                  socketTimeoutMillis=2000"/>

<cache alias="myCache">
  <key-type>java.lang.String</key-type>
  <value-type>java.lang.String</value-type>
  <diskPersistent>true</diskPersistent>
  <diskSpoolBufferSizeMB>30</diskSpoolBufferSizeMB>
  <maxEntriesLocalDisk>1000</maxEntriesLocalDisk>
  <cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
                              properties="replicateAsynchronously=true,
                                          replicatePuts=true,
                                          replicateUpdates=true,
                                          replicateUpdatesViaCopy=false,
                                          replicateRemovals=true"/>
</cache>

在上述配置中,我们首先定义了一个 RMI 的缓存管理器提供者工厂 RMICacheManagerPeerProviderFactory 和一个 RMI 的缓存管理器监听器工厂 RMICacheManagerPeerListenerFactory,并分别指定了自动发现、组播地址、端口等相关参数。

接着,我们定义了名为 myCache 缓存添加了一个 cacheEventListenerFactory 属性,用于定义缓存事件的处理方式,即使用 RMICacheReplicatorFactory 实现缓存数据的复制和同步。

需要注意的是,分布式缓存的配置和使用与单机缓存有所不同,同时也需要考虑到网络通信等问题,因此更加复杂和耗费资源。在选择是否需要使用分布式缓存时,需要权衡其带来的性能、复杂性和成本等因素。

Guess you like

Origin juejin.im/post/7237389821101523005