Jetcache: Ali's multi-level cache framework must be mastered

0 Preface

We explained the local cache ehcache component before. In practical applications, local cache or redis is not a single use, but a combination is used to meet different business scenarios. Therefore, how to elegantly combine local cache and remote cache becomes our The problem to be studied, and this point, Alibaba's open source jetcachecomponents help us realize

1. Introduction to jetcache

jetcacheIt is an open source Java-based caching framework developed by Ali, which supports multiple cache types: local cache, distributed cache, and multi-level cache. It can meet the caching requirements of different business scenarios.

Jetcache has the characteristics of easy to use, high performance and strong scalability. Support cache preheating, cache key prefix and other functions. Combined with spring-cache, a very elegant cache type switching can be achieved

Official website address: https://github.com/alibaba/jetcache
Official documentation: https://github.com/alibaba/jetcache/tree/master/docs/CN

2. Use of jetcache

1. Introduce dependencies. Here we use the springboot project framework and redis as a remote cache. So we introduced jetcache-starter-redisdependencies, here my springboot version is2.6.13

If it is a non-springboot project, you can refer to the official website for configuration

insert image description here

<dependency>
            <groupId>com.alicp.jetcache</groupId>
            <artifactId>jetcache-starter-redis</artifactId>
            <version>2.7.0</version>
</dependency>

<!--        jetcache2.7.x版本需要额外添加该依赖-->
<dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>4.3.1</version>
</dependency>

The corresponding version description is as follows: springboot and jetcache version relationship

insert image description here

2. Modify the configuration file, configure the redis address and the number of threads

jetcache:
  # 统计间隔,0表示不统计,开启后定期在控制台输出缓存信息
  statIntervalMinutes: 15
  # 是否把cacheName作为远程缓存key前缀
  areaInCacheName: false
  # 本地缓存配置
  local:
    default: # default表示全部生效,也可以指定某个cacheName
      # 本地缓存类型,其他可选:caffeine/linkedhashmap
      type: linkedhashmap
      keyConvertor: fastjson
  # 远程缓存配置
  remote:
    default: # default表示全部生效,也可以指定某个cacheName
      type: redis
      # key转换器方式n
      keyConvertor: fastjson
      broadcastChannel: projectA
      # redis序列化方式
      valueEncoder: java
      valueDecoder: java
      # redis线程池
      poolConfig:
        minIdle: 5
        maxIdle: 20
        maxTotal: 50
      # redis地址与端口
      host: 127.0.0.1
      port: 6379

For more detailed parameter configuration, please refer to the official website description: https://github.com/alibaba/jetcache/blob/master/docs/CN/Config.md

insert image description here

3. Add annotations to the startup class @EnableCreateCacheAnnotation, enable caching, add @EnableMethodCache(basePackages = "com.example.jetcachedemo")annotations, and configure the caching method scan path

4. There are three ways to use the cache:

  • Method 1 (recommended) AOP mode: via @Cached, @CacheUpdate, @CacheInvalidateannotations
@RestController
@RequestMapping("user")
public class UserController {
    
    

    @GetMapping("getRemote")
    @Cached(name="userCache:", key = "#id", expire = 3600, timeUnit = TimeUnit.SECONDS, cacheType = CacheType.REMOTE)
    public User getRemote(Long id){
    
    
        // 直接新建用户,模拟从数据库获取数据
        User user = new User();
        user.setId(id);
        user.setName("用户remote"+id);
        user.setAge(23);
        user.setSex(1);
        System.out.println("第一次获取数据,未走缓存:"+id);
        return user;
    }

    @GetMapping("getLocal")
    @Cached(name="userCache:", key = "#id", expire = 3600, timeUnit = TimeUnit.SECONDS, cacheType = CacheType.LOCAL)
    public User getLocal(Long id){
    
    
        // 直接新建用户,模拟从数据库获取数据
        User user = new User();
        user.setId(id);
        user.setName("用户local"+id);
        user.setAge(23);
        user.setSex(1);
        System.out.println("第一次获取数据,未走缓存:"+id);
        return user;
    }

    @GetMapping("getBoth")
    @Cached(name="userCache:", key = "#id", expire = 3600, timeUnit = TimeUnit.SECONDS, cacheType = CacheType.BOTH)
    public User getBoth(Long id){
    
    
        // 直接新建用户,模拟从数据库获取数据
        User user = new User();
        user.setId(id);
        user.setName("用户both"+id);
        user.setAge(23);
        user.setSex(1);
        System.out.println("第一次获取数据,未走缓存:"+id);
        return user;
    }

    @PostMapping("updateUser")
    @CacheUpdate(name = "userCache:", key = "#user.id", value = "#user")
    public Boolean updateUser(@RequestBody User user){
    
    
        // TODO 更新数据库
        return true;
    }

    @PostMapping("deleteUser")
    @CacheInvalidate(name = "userCache:", key = "#id")
    public Boolean deleteUser(Long id){
    
    
        // TODO 从数据库删除
        return true;
    }

}

It should be noted here that the entity class Usermust implement serialization, that is, declareSerializable

@Data
public class User implements Serializable {
    
    

    private Long id;
    private String name;
    private Integer age;
    private Integer sex;
}
  • Method 2 API mode: pass @CreateCache, note: the CreateCache annotation in jetcache 2.7 version is obsolete and not recommended
@RestController
@RequestMapping("user2")
public class User2Controller {
    
    

    @CreateCache(name= "userCache:", expire = 3600, timeUnit = TimeUnit.SECONDS, cacheType = CacheType.BOTH)
    private Cache<Long, Object> userCache;

    @GetMapping("get")
    public User get(Long id){
    
    
        if(userCache.get(id) != null){
    
    
            return (User) userCache.get(id);
        }
        User user = new User();
        user.setId(id);
        user.setName("用户both"+id);
        user.setAge(23);
        user.setSex(1);
        userCache.put(id, user);
        System.out.println("第一次获取数据,未走缓存:"+id);
        return user;
    }

    @PostMapping("updateUser")
    public Boolean updateUser(@RequestBody User user){
    
    
        // TODO 更新数据库
        userCache.put(user.getId(), user);
        return true;
    }

    @PostMapping("deleteUser")
    public Boolean deleteUser(Long id){
    
    
        // TODO 从数据库删除
        userCache.remove(id);
        return true;
    }

}
  • Method 3 Advanced API mode: pass CacheManager, only version 2.7 can be used

(1) Add dependencies

<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.25</version>
</dependency>

(2) Write configuration class

@Configuration
public class JetcacheConfig {
    
    

    @Autowired
    private CacheManager cacheManager;
    private Cache<Long, Object> userCache;

    @PostConstruct
    public void init(){
    
    
        QuickConfig qc = QuickConfig.newBuilder("userCache:")
                .expire(Duration.ofSeconds(3600))
                .cacheType(CacheType.BOTH)
                // 本地缓存更新后,将在所有的节点中删除缓存,以保持强一致性
                .syncLocal(false)
                .build();
        userCache = cacheManager.getOrCreateCache(qc);
    }

    @Bean
    public Cache<Long, Object> getUserCache(){
    
    
        return userCache;
    }
}

(3) Call code

@RestController
@RequestMapping("user3")
public class User3Controller {
    
    

    @Autowired
    JetcacheConfig jetcacheConfig;
    @Autowired
    private Cache<Long, Object> userCache;

    @GetMapping("get")
    public User get(Long id){
    
    
        if(userCache.get(id) != null){
    
    
            return (User) userCache.get(id);
        }
        User user = new User();
        user.setId(id);
        user.setName("用户both"+id);
        user.setAge(23);
        user.setSex(1);
        userCache.put(id, user);
        System.out.println("第一次获取数据,未走缓存:"+id);
        return user;
    }

    @PostMapping("updateUser")
    public Boolean updateUser(@RequestBody User user){
    
    
        // TODO 更新数据库
        userCache.put(user.getId(), user);
        return true;
    }

    @PostMapping("deleteUser")
    public Boolean deleteUser(Long id){
    
    
        // TODO 从数据库删除
        userCache.remove(id);
        return true;
    }

}

In the form of multi-level cache, the data will be obtained from the local cache first, and if it cannot be obtained locally, it will be obtained from the remote cache

5. Start redis and start the demo project

Note that if an error occurs NoClassDefFoundError: redis/clients/util/Poolor NoClassDefFoundError: redis/clients/jedis/UnifiedJedisan error is reported during startup, it means that the versions of springboot and jetcache are inconsistent. For the corresponding relationship, please refer to the description in the first step above.
At the same time, if you are using version 2.7.x of jetcache, because there is a dependency on the jedis package in this version, additional Add the following dependencies, or reduce the jetcache version to below 2.6.5

<dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>4.3.1</version>
</dependency>

3. Test

3.1 Method 1 Test

1. Visit localhost:8088/user/getRemote?id=1

insert image description here

Because the remote cache is configured, the corresponding key can also be seen in redis

insert image description here

2. Visit localhost:8088/user/getLocal?id=1. This method is obtained from the local cache. Now only the remote cache has data. We call and find that the cache data is still obtained. This means that when we configure in the configuration file After the local cache and the remote cache are configured, the local cache and the remote cache will automatically call each other in method 1

For example, the local cache has this key, but not in redis. When accessing through remote cache, it will be obtained from redis first. If not, the local cache will be automatically obtained, but the data is still stored in the local cache and will not be synchronized to redis. This is more Flexible implementation of multi-level cache architecture

insert image description here

3.2 Method 2 test

1. Re-test CreateCachethe form: localhost:8088/user2/get?id=4

insert image description here

It is obtained normally, and there is a corresponding value in redis

insert image description here
And when we change the cache mode to LOCAL, then visit localhost:8088/user2/get?id=5

@CreateCache(name= "userCache:", expire = 3600, timeUnit = TimeUnit.SECONDS, cacheType = CacheType.LOCAL)

You will find that there is no corresponding cache in redis, and it only exists in the local cache, indicating that we have successfully specified the form of the local cache

insert image description here

3.3 Method 3 test

1. Call localhost:8088/user3/get?id=11

insert image description here

The cache setting in redis is successful!

insert image description here

4. Common errors

1. ClassNotFoundException: com.alibaba.fastjson.JSON
Solution: add dependencies

<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.25</version>
</dependency>

2. NoClassDefFoundError: redis/clients/jedis/UnifiedJedis
Solution:
add dependencies

<dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>4.3.1</version>
</dependency>

Or reduce the jetcache version to below 2.6.5

Demo source code

https://gitee.com/wuhanxue/wu_study/tree/master/demo/jetcache-demo

Guess you like

Origin blog.csdn.net/qq_24950043/article/details/130478473