Spring Boot 2.5 集成缓存框架 JetCache 2.7

1 摘要

JetCache 是阿里出品的一款缓存框架,其提供了基于接口的缓存以及能够实现本地和远程二级缓存(本地缓存是当前 JVM 中的LinkedHashMap,远程缓存为Redis),在提高系统吞吐量的同时,也极大地方便了代码编写。本文将介绍基于 Spring Boot 2.5 简易集成缓存框架 JetCAche 2.7。

JetCache Github: https://github.com/alibaba/jetcache

2 核心 Maven 依赖

./demo-mybatis-plus/pom.xml
        <!-- jetcache 缓存框架 -->
        <dependency>
            <groupId>com.alicp.jetcache</groupId>
            <artifactId>jetcache-starter-redis-lettuce</artifactId>
            <version>${jetcache.version}</version>
        </dependency>

其中版本信息为:

        <jetcache.version>2.7.3</jetcache.version>

本示例中使用的是 lettuce 作为 Redis 连接工具

3 核心代码

3.1 JetCache 配置
./demo-mybatis-plus/src/main/resources/application.yml
# jetCache
jetcache:
  statIntervalMinutes: 1
  areaInCacheName: false
  local:
    jetcache-demo:
      type: linkedhashmap
      keyConvertor: jackson
  remote:
    jetcache-demo:
      type: redis.lettuce
      keyConvertor: jackson
      broadcastChannel: jetcache-demo
      keyPrefix: jetcache-demo
      valueEncoder: java
      valueDecoder: java
      uri: redis://your_password@127.0.0.1:6379
      defaultExpireInMillis: 5000
      poolConfig:
        minIdle: 5
        maxIdle: 20
        maxTotal: 50

注意:

jetcache.local.jetcache-demo: 这里的 jetcache-demo 是作为缓存区域(area)命名的,必须与代码中的 area 定义保持一致,否则就会启动失败,抛出com.alicp.jetcache.CacheConfigException: no local cache builder 异常。

同理 jetcache.remote.jetcache-demojetcache-demo 也是一样的。一般同一个项目(业务模块)使用同样的 area 命名。

官方配置信息说明:

https://github.com/alibaba/jetcache/blob/master/docs/CN/Config.md

3.2 常用注解

官方文档注解说明:

https://github.com/alibaba/jetcache/blob/master/docs/CN/MethodCache.md

一些常用注解的补充说明:

@Cached:标记缓存注解,用于方法上,一般用于查询方法上常用参数包括:

注解参数说明
area区域,必须定义的与 application 里边的保持一致(local/remote 后一级的参数名),否则运行报错
name缓存的 key(前缀)
key缓存的key后边部分,拼在name后边,可缺省,定义的key必须在请求参数中包括,否则报错,无法缓存
expire缓存失效时间,默认单位:秒
localExpire本地缓存失效时间,默认单位:秒
cacheType缓存类型,有本地,远程和两者都包含可选,简易使用CacheType.BOTH
localLimit本地缓存数量限制

@CacheInvalidate:缓存失效注解,用于方法上,一般用于更新或者删除方法上。

@CachePenetrationProtect: 指定未命中缓存的情况下,同一个 JVM 同一个 key 只有一个线程去操作,用于防止并发请求。

3.3 注解使用示例
./demo-mybatis-plus/src/main/java/com/ljq/demo/springboot/mybatisplus/service/impl/JetCacheUserServiceImpl.java
package com.ljq.demo.springboot.mybatisplus.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.StrUtil;
import com.alicp.jetcache.anno.CacheInvalidate;
import com.alicp.jetcache.anno.CachePenetrationProtect;
import com.alicp.jetcache.anno.CacheType;
import com.alicp.jetcache.anno.Cached;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ljq.demo.springboot.mybatisplus.common.constant.JetCacheConst;
import com.ljq.demo.springboot.mybatisplus.dao.UserDao;
import com.ljq.demo.springboot.mybatisplus.model.entity.UserEntity;
import com.ljq.demo.springboot.mybatisplus.model.param.user.*;
import com.ljq.demo.springboot.mybatisplus.service.IJetCacheUserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.Objects;

/**
 * @Description: JetCache 缓存示例用户业务实现类
 * @Author: junqiang.lu
 * @Date: 2023/3/25
 */
@Service
public class JetCacheUserServiceImpl extends ServiceImpl<UserDao, UserEntity> implements IJetCacheUserService {


    /**
     * 保存(单条)
     *
     * @param userSaveParam
     * @return
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    @Override
    public UserEntity save(UserSaveParam userSaveParam) {
        // 请求参数获取
        UserEntity userParam = new UserEntity();
        BeanUtil.copyProperties(userSaveParam,userParam,CopyOptions.create().ignoreNullValue().ignoreError());
        // 保存
        String nowTime = String.valueOf(System.currentTimeMillis());
        userParam.setUserInsertTime(nowTime);
        userParam.setUserUpdateTime(nowTime);
        super.save(userParam);
        return userParam;
    }

    /**
     * 查询详情(单条)
     *
     * @param userInfoParam
     * @return
     */
    @CachePenetrationProtect
    @Cached(area = JetCacheConst.CACHE_AREA, name = ":getUserById", key = "#userInfoParam.id",
            cacheType = CacheType.BOTH, expire = 300, localExpire = 180)
    @Override
    public UserEntity info(UserInfoParam userInfoParam) {
        return super.getById(userInfoParam.getId());
    }

    /**
     * 查询列表
     *
     * @param userListParam
     * @return
     */
    @Override
    public IPage<UserEntity> list(UserListParam userListParam) {
        LambdaQueryWrapper<UserEntity> userWrapper = Wrappers.lambdaQuery();
        userWrapper.likeRight(StrUtil.isNotBlank(userListParam.getUserName()), UserEntity::getUserName,
                userListParam.getUserName());
        IPage<UserEntity> page = new Page<>(userListParam.getCurrentPage(),userListParam.getPageSize());
        userWrapper.orderBy(true, Objects.isNull(userListParam.getAscFlag()) ?
                        false : userListParam.getAscFlag(), UserEntity::getId);
        return super.page(page,userWrapper);
    }

    /**
     * 更新(单条)
     *
     * @param userUpdateParam
     * @return
     */
    @CacheInvalidate(area = JetCacheConst.CACHE_AREA, name = ":getUserById", key = "#userUpdateParam.id")
    @Override
    public UserEntity update(UserUpdateParam userUpdateParam) {
        LambdaQueryWrapper<UserEntity> userWrapper = new LambdaQueryWrapper<>();
        userWrapper.eq(true, UserEntity::getId, userUpdateParam.getId());
        int countUser = super.count(userWrapper);
        if (countUser < 1) {
            return null;
        }
        // 请求参数获取
        UserEntity userParam = new UserEntity();
        BeanUtil.copyProperties(userUpdateParam, userParam, CopyOptions.create().ignoreNullValue().ignoreError());
        userParam.setUserUpdateTime(String.valueOf(System.currentTimeMillis()));
        super.updateById(userParam);
        return userParam;
    }

    /**
     * 删除(单条)
     *
     * @param userDeleteParam
     * @return
     */
    @CacheInvalidate(area = JetCacheConst.CACHE_AREA, name = ":getUserById", key = "#userDeleteParam.id")
    @Override
    public boolean delete(UserDeleteParam userDeleteParam) {
        boolean deleteFlag = super.removeById(userDeleteParam.getId());
        if (!deleteFlag) {
            return false;
        }
        return true;
    }
}

注意事项:

这里对于同一业务模块的areaname 要保持一致。area 可以用一个常量类来保存,name 可以在前边添加: 这样在Redis数据库中可以分级显示。

4 使用效果

JetCache

说明:

由于 JetCache 默认使用的是 Java提供的序列化,所以在 Redis 中看到的效果就是这样的,JetCache 2.7 版本官方不提供 Json 序列化方式,需要自行实现(后期再来更新)。

5 使用总结

  • 1 JetCache 适用于标准的查询单条数据或者查询列表数据的接口进行完整的缓存,这就要求接口的入参必须包含唯一健,且返回值必须是DTO对象。同时对于分页查询的接口无法实现缓存,也没有意义。
  • 2 JetCache 默认使用的是 JDK 提供的序列化,JDK序列化性能差,可读性差。在目前项目开发过程中,一般 Redis 缓存都使用 JSON 格式对 Value 进行序列化。
  • 3 JetCache 最大的优势是实现了本地和远程的二级缓存,相较于使用 Redis 缓存,使用本地缓存能够提供更高的吞吐量。

6 推荐参考资料

JetCache官方文档

记录使用JetCache遇到的问题

7 Github 源码

Gtihub 源码地址 : https://github.com/Flying9001/springBootDemo

个人公众号:404Code,分享半个互联网人的技术与思考,感兴趣的可以关注.
404Code

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值