The story background

As the price of hardware goes down, people rely more and more on hardware. I’ve even heard that code doesn’t matter, so add machines if you can’t. For example, the use of cache, usually based on virtual machine memory, disk storage, middleware (Redis memory) and other ways, we all know that the most suitable is the best, but in practice, often is always directly on Redis.

So is Redis necessarily the best choice? Not to mention the memory requirements, from efficiency and performance, is not necessarily optimal. Therefore, using different cache strategies for different scenarios is what experts should pursue.

This article introduces you to another caching framework besides Redis: EhCache. I decided to write because I was using this framework in the project and had some problems, so I decided to look into it further. After research, it turns out to be a bit interesting

EhCache profile

EhCache is a pure Java in-process cache framework, with the characteristics of fast and lean. Note that the keyword process here, process-based caching intuition tells us that it must be more efficient because it operates directly within the process, but the sharing of the cache between different applications can be problematic.

EhCache is the default CacheProvider in Hibernate and is supported by Spring Boot. The cache abstraction provided in Spring also supports binding to the EhCache framework and supports annotation-based use. Therefore, EhCache is a widely used Java-based caching framework that is very easy to use.

EhCache provides a variety of caching strategies, mainly divided into memory and disk levels. EhCache is a caching framework for general-purpose caches, Java EE and lightweight containers.

The characteristics of EhCache

A brief description of the framework’s features:

  • Simple, fast, with a variety of caching strategies;
  • There are two levels of cached data: memory and disk, without worrying about capacity;
  • Cached data is written to disks during VM restart.
  • Distributed caching can be implemented through RMI, pluggable API, etc.
  • Listener interfaces with caches and cache managers;
  • Support for multiple cache manager instances, as well as multiple cache areas for an instance, and provide Hibernate caching implementation;

EhCache can be used alone, but is usually used in conjunction with Mybatis, Shiro and other tripartite libraries. EhCache is used in combination with Shiro in my project.

In addition to the advantages, EhCache also has some disadvantages. For example, it takes up a lot of disk space because DiskCache’s algorithm is simple and simply appends storage to elements directly. This improves efficiency, but on heavily used systems, disks fill up quickly.

In addition, data security is not guaranteed, and of course, there may be conflicts when a Java process is suddenly killed. EhCache resolves conflicts by reconstructing the Cache, which may affect Cache data retention. The Cache is only a simple acceleration and cannot guarantee data security.

EhCache with Redis

EhCache is directly cached in the JVM, which is fast and efficient. Compared with Redis, EhCache is simple to operate, easy to use and efficient. Although EhCache also provides cache sharing scheme, it does not support distributed cluster well, and cache sharing is difficult to implement.

Redis is through Socket access to the cache service, the efficiency is lower than EhCache, much faster than the database, processing cluster and distributed cache is convenient, there is a mature solution.

Therefore, EhCache can be used if the application is a single application or requires high cache access. If a large system has cache sharing, distributed deployment, and large cache content, Redis is recommended.

EhCache architecture diagram

Take a look at the EhCache architecture diagram to get an idea of its components.

The Cache Replication section provides a mechanism for Cache Replication for distributed environments. EhCache was originally an independent component of local Cache framework. In the later development, combined with Terracotta service array model, EhCache can support distributed Cache cluster, mainly including RMI, JGroups, JMS and Cache Server for communication between nodes.

In-process APIs provide support based on JSR, JMX and other standards, which can be compatible and transplanted well, and have a perfect monitoring and management mechanism for all kinds of objects.

Network APIs provide external support based on RESTful APIs, JMS APIs, and Cache Server.

The Core you need to focus on during use is the Core in the middle. It contains core apis and concepts:

  • CacheManager: a CacheManager that can be created in singleton or multiple instances and is an entry class for Ehcache.
  • Cache: Each CacheManager can manage multiple caches. Each Cache can manage multiple elements in hash mode. All caches implement the Ehcache interface.
  • Element: The component of a single piece of cached data, used to hold the actual cached content.

The management of the three can be shown in the following figure:

Cache expiration policy

In the architecture diagram, Memory Store LRU, Memory Store LFU, Memory Store FIFO and other Memory storage algorithms can be seen. That is, when the cache footprint approaches a critical value, the above elimination strategy is used to clean up some data.

EhCache provides three elimination algorithms:

  • FIFO: First In First Out. Determine how long the data has been stored, and the data farthest away from the present is eliminated first.
  • LRU: Least Recently Used Determine the most recently used data, and the most recent data is eliminated first.
  • LFU: The Least Frequently Used. The data that has been used the least over a period of time will be eliminated first.

Ehcache adopts the lazy elimination mechanism. Each time data is put into the cache, it saves a time. When reading data, it compares the TTL with the set time to determine whether it is expired.

EhCache analysis

Now that you know the basics, let’s experiment with EhCache. Ehcache2. x and EHCache3. x have different usage.

The relatively new 3.9.6 version is used here, and different versions of the API can be used differently.

Using EhCache based on the API

EhCache provides apis and XML for creating CacheManger and Cache. Let’s look at the API-based form first:

Introduce EhCache dependencies in POM files:

<dependency> <groupId>org.ehcache</groupId> <artifactId> Ehcache </artifactId> <version>3.9.6</version> </dependency>Copy the code

Create and use the following code:

Public class EhCacheTest {@test public void Test () {// 1. Use CacheManagerBuilder to create a pre-configured Cache. The first parameter is the alias and the second parameter is used to configure the Cache. // build method and initialize; The true parameter in build indicates initialization. CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() .withCache("preConfigured", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(100)).build()) .build(true); // Get back the pre-configured, type-safe values for key and value types, otherwise a ClassCastException will be thrown. Cache<Long, String> preConfigured = cacheManager.getCache("preConfigured", Long.class, String.class); System.out.println(" Getting the key from the cache is preConfigured: "+ preConfigured); // Create a Cache using CacheManager as required. Instantiated and fully instantiated caches are returned through the Cachemanager.getCache API. Cache<Long, String> myCache = cacheManager.createCache("myCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(100)).build()); MyCache. Put (1L, "da one!" ); String value = mycache.get (1L); System.out.println(" get key from cache: 1L: "+ value); // The close method releases cache resources managed by CacheManager. Cachemanager.close (); }}Copy the code

The above code shows how to create a CacheManager and Cache, set and obtain the Cache in API form.

Using EhCache based on XML

Create the ehcache.xml configuration file in SRC /main/resources/.

<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>
Copy the code

The differences between the 3.x version and the 2.x version are evident in the XML configuration files. 2. X uses the Ehcache element as the root node, while 3.x uses config as the root node.

There are three parts in the XML above:

  • Common cache cache-foo: cache whose alias is foo and whose key-value type is String. If not specified, the default is Object.
  • Cache-template: Implements a configuration abstraction that can be extended in the future;
  • Cache-bar: uses cache-template template myDefaults and overwrites the key-type type. The key-type of myDefaults is Long and overwrites the Number type.

Other attributes and elements in the cache:

  • Name is a name.
  • Alias indicates an alias.
  • Key-type specifies the key type.
  • Value-type Specifies the type of value.
  • Heap specifies the format of entities that can be created in the heap, where unit=”entries” indicates that the following 20 are entities;
  • Offheap means that up to 10 MB of heap memory can be allocated before the obsolete cache items begin to be eliminated;
  • Uses-template indicates the name of a template.

Of course, it is also possible to configure cached directories via the Persistence element. The use of other attributes can be explored slowly.

Using EhCache based on Spring Boot

As mentioned earlier, Spring supports caching, and Spring Boot also supports automatic configuration of caching. The following is based on Spring Boot to complete the EhCache integration and use case demonstration.

Introduce the corresponding starter in Spring Boot:

<! -- Ehcache dependency --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>org.ehcache</groupId> The < artifactId > ehcache < / artifactId > < version > 3.9.6 < / version > < / dependency >Copy the code

Add the following configuration to the application.properties configuration:

spring.cache.ehcache.config=ehcache.xml
Copy the code

Add the @enablecaching annotation to the Spring Boot bootstrap class:

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

Create a user cached entity class Person:

@Data
public class Person {
​
  public Person(int id,String name){
    this.id = id;
    this.name = name;
  }
​
  private int id;
​
  private String name;
}
Copy the code

The corresponding Service method is implemented:

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"); }}Copy the code

The @cacheable annotation provided by Spring specifies the name of the cache as personCache and the key as ID. The log is printed inside the method, or if called inside the method.

Writing unit test classes:

@Slf4j @SpringBootTest @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class PersonServiceTest { @Resource private  PersonService personService; @ org. Junit. Jupiter. API. The Order (1) @ Test void testCache () throws InterruptedException {the info (" first data query id = 1 "); personService.getById(1); Log.info (" second query for data with ID =1 "); personService.getById(1); Thread.sleep(3000); }}Copy the code

The following logs are generated when the corresponding methods are invoked twice:

C.S.S.P ersonServiceTest: first query id = 1 data C.S.S.I.P ersonServiceImpl: query user id = 1 C.S.S.P ersonServiceTest: second query data id = 1Copy the code

As you can see, the first time you enter the method to query, the second time you remove the cache.

There are many more ways and scenarios for using the cache annotations provided by Spring that I won’t cover here.

summary

Since I happened to use this technology in my work, I studied and wrote an article to introduce the basic knowledge, technical architecture, usage scenarios, API usage and integration based on Spring Boot of EhCache. Overall, it’s an entry level, and you can expand on it. The distributed support of EhCache is not covered in this article, mainly because it is not that easy to use, so you can study it yourself if you are interested.

About the blogger: Author of the technology book SpringBoot Inside Technology, loves to delve into technology and writes technical articles.

Public account: “program new vision”, the blogger’s public account, welcome to follow ~

Technical exchange: Please contact the weibo user at Zhuan2quan