1 introduction

When it comes to caching, Redis is probably the first thing that comes to mind. As a non-relational database based on key-value pairs, Redis has high performance, rich data structure, persistence, high availability, distribution and other characteristics, making it widely recognized and used in the industry. However, the use of Redis inevitably involves network connection. When the network connection is unstable or time-consuming, it will inevitably affect our business use. If we want to improve our business performance while reducing our dependence on other machines, using local caching is a good choice.

When using local caching, most of the time we use ConcurrentHashMap. For the use of local cache, there are several mature local cache tools such as EhCache, Guava Cache, and Caffeine. Ehcache can be used when caching needs to be persisted. If you do not have persistence, you can use Guava Cache or Caffeine. Caffeine is a secondary optimization based on Guava Cache. You can choose which local cache tool to use according to your business needs. This article will explain the Guava Cache used in DRS systems.

In this project, the selected Guava cache version information is as follows:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>30.1 the jre</version>
</dependency>
Copy the code

2 Introduction to Guava Cache

Guava Cache and ConcurrentHashMap are similar in that they both use key-value pairs to store data and use segmental locking to improve concurrent operations in multiple threads. In contrast, when using ConcurrentHashMap for cache storage, the cache data is retained until the system restarts or a message is displayed indicating that the cache is deleted. Guava Cache supports the setting of cache expiration time and can be used to clear the cache. The guava cache also automatically loads data from the database into the cache. Instead of checking whether the cache exists every time we fetch it, the Guava Cache loads the data according to the way we set it. In addition, Guava Cache can perform statistical operations such as the cache hit ratio, the average time to load new values, and so on. It is because of these characteristics that Guava Cache was selected for use in DRS systems. Let’s take a look at how we use Guava Cache for Cache management operations.

3 Cache expiration time

Guava Cache supports three expiration Settings: volume-based collection, timed collection, and reference-based collection. Obviously, voluence-based and reference-based recycling is independent of cache time. When we need to set the cache cache expiration time, use is necessarily timing recovery recovery strategy, then, in the guava cache, supports two timing recovery strategy, respectively is based on the read/write access to recovery and recycling based on write access, is cached in the set period of time has not been read and write access, or write access to the cache will be recycled, This feature is sufficient for our expiration time Settings. ExpireAfterAccess (long, TimeUnit) : expireAfterWrite(long, TimeUnit) : expireAfterAccess(long, TimeUnit) : expireAfterWrite(long, TimeUnit) : Write access-based reclamation, that is, the cache item is not write accessed within a given period of time, then the reclamation is as follows. The write access-based reclamation policy is given, and the expiration time is set to 3 seconds:

Cache<String, String> cache = CacheBuilder.newBuilder()
    .expireAfterWrite(3, TimeUnit.SECONDS)
    .build();
Copy the code

However, in our project, we need to set different expiration times for different keys. In the preceding example, only one expiration time can be set. The expiration time of all keys is the same. To meet the requirements of different expiration times, we use a two-level Guava cache, as shown below:

private Cache<String, Cache<String, String>> cacheCache = CacheBuilder.newBuilder().build();
     
public void init(a) {
    Cache<String, String> cache1 = CacheBuilder.newBuilder()
        .expireAfterWrite(3, TimeUnit.SECONDS)
        .build();
    cache1.put("appName"."drs-server");
    Cache<String, String> cache2 = CacheBuilder.newBuilder()
        .expireAfterWrite(2, TimeUnit.SECONDS)
        .build();
    cache2.put("appName"."drs-ops");
    cacheCache.put("key1", cache1);
    cacheCache.put("key2", cache2);
}
Copy the code

In the above example, we can also use the Map+ Guava cache method, i.e. a Map with a Guava cache, as defined below:

Map<String, Cache<String, String>> cacheMap = new HashMap<String, Cache<String, String>>();
Copy the code

Why do we use a two-level cache instead of a Map+cache? The reason is that we want to load data from the database automatically when the data is not in the cache, so using a two-level cache structure is more appropriate. This brings us to the loading mechanism of the Guava cache.

4 Cache loading mechanism

Guava cache can be loaded in two ways. One is to set the loading mode of the cache in the build method when the cache object is built (only LocalCache objects are available). The other is to set the loading mode by implementing the Callable interface method when the cache object is obtained. Build the cache object as follows:

LoadingCache<String, String> cache = CacheBuilder.newBuilder()
    .build(new CacheLoader<String, String>() {
        @Override
        public String load(String s) throws Exception {
            return "load: " + UUID.randomUUID().toString().substring(1.6); }});Copy the code

When the get(K key) method is used to obtain the LocalCache object, if the cache value corresponding to the specified key does not exist, the object is loaded from the load method. However, if the getIfPresent method is used, a null value is returned instead of loading the cache value from the executing load method. When the cache is fetched with get(K key, Callable call), if the corresponding cache value does not exist, the cache is loaded from the implementation of the Callable interface method. The complete sample code is shown below:

public static void main(String[] args) throws Exception{
    LoadingCache<String, String> cache = CacheBuilder.newBuilder()
        .build(new CacheLoader<String, String>() {
            @Override
            public String load(String s) throws Exception {
                return "load: " + UUID.randomUUID().toString().substring(1.6); }}); System.out.println(cache.getIfPresent("appName1"));
    System.out.println(cache.get("appName2"));
 
    String loadValue = cache.get("appName3".new Callable<String>() {
        @Override
        public String call(a) throws Exception {
            return "call: " + UUID.randomUUID().toString().substring(1.6); }}); System.out.println(loadValue); }Copy the code

The test results are as follows:

5 Cache Clearing

Now that we have set the cache expiration time, how do we clean up after the cache expires? Instead, the cache is cleaned up on the next read or write access by comparing the current time with the time when the cache was loaded. If the time difference is greater than the given expiration time, the cache is cleaned up. If the loading mode of the cache is set, the cache value is reloaded and the loading time of the cache is updated. Cache cleanup is implemented in the get method, using the following methods:

get(Object key, int hash); // Get the cache value
getLiveEntry(Object key, int hash, long now); // Get the living object
isExpired(ReferenceEntry<K, V> entry, long now); // Determine whether it expires
expireEntries(long now); // Clear the cache
Copy the code

The logic for judging whether the expiration date is as follows:

If the cache has expired, return true, and then perform a cache cleanup delete, as shown below:

Besides, guava cache also supports cache some of the statistical work, such as statistical cache hit ratio, load, the new value of the average time, the total number of cached items be recycled, etc., and cache interrupt operation and refresh operation, because of this project is not used to these features, there is no in-depth understanding, anyone interested in if you have to use, You can go to the official website for in-depth understanding, see detailsIfeve.com/google-guav…