SpringBoot 集成 Caffeine+Redis实现一二级缓存,同时可以设置本地缓存Caffeine开关

目录

总体思路

业务流程图

一、本地缓存Caffeine和redis 依赖以及Redis配置

 二、代码逻辑及步骤

1、配置本地缓存Caffeine 配置类开关和redis Bean 

2、配置本地缓存用到的常量

3、本地缓存 初始化以及 接口实现方法。

(1)请求接口,  这个返回值 HotelUnit  可以根据 自己的业务定义返回值,我这里是用自己的业务返回值,你可以用String, Integer,对象接受,看你自己怎么存的业务。

 (2)caffeine 实现类,以及初始化。

 (3)Redis实现类

 (4)测试类 

(5)执行结果演示



注意:不过多介绍基本知识,需要先自行了解 本地缓存caffeine和 Redis 相关基本知识,主讲思路和代码。

总体思路

级别: L1 Caffeine JVM 级别缓存 , L2 Redis 缓存

1、服务启动。

2、读取配置文件中本地缓存开关是否。

A、本地缓存开关打开true,请求进来 --> 读写一级缓存L1 -->判断L1是否存在,存在那么返回结果。L1不存在,那么在进行 L2判断是否存在 -->存在,写入L1,返回结果;L2不存在 -->读取数据库-->写入L2-->写入L1-->返回结

B、本地缓存开关关闭false,请求进来 -->  L2判断是否存在 -->存在,返回结果;L2不存在 -->读取数据库-->写入L2-->返回结果。

业务流程图

一、本地缓存Caffeine和redis 依赖以及Redis配置

 <!-- Redis缓存 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.6.1</version>
        </dependency>

        <!-- 咖啡因缓存 -->
        <dependency>
            <groupId>com.github.ben-manes.caffeine</groupId>
            <artifactId>caffeine</artifactId>
            <version>2.9.1</version>
        </dependency>

 yml配置文件

redis:
  database: 14 # 几号库
  host: 192.168.1.94
  port: 6379
  password:
  lettuce:
    pool:
      max-active: 8 # 最大连接
      max-wait: -1ms # 连接等待时间
      max-idle: 8 # 最大空闲连接
      min-idle: 0 # 最小空闲连接
# 本地缓存开关
com:
  huwei:
    hotel:
      collector:
        cache: 
          hotelunit:
            local-cache:
              enabled: true

 二、代码逻辑及步骤

注意:redis中 put  hash类型的数据,本文不做演示。

1、配置本地缓存Caffeine 配置类开关和redis Bean 

 @ConditionalOnProperty注解来控制@Configuration配置类是否生效,不懂请自行百度。

**
 * @author ljy
 * @date 2023/7/25
 */
@Configuration(proxyBeanMethods = false)
public class HotelUnitCacheConfiguration {

    @Bean
    public HotelUnitRedisCache hotelUnitRedisCache(){
        return new HotelUnitRedisCache();
    }

   //配置本地缓存bean 开关
    @Configuration
    @ConditionalOnProperty(name = HotelUnitCacheProperties.LOCAL_CACHE_PREFIX + ".enabled", havingValue = "true", matchIfMissing = false)
    @EnableConfigurationProperties(HotelUnitCacheProperties.class)
    static class LocalCacheConfiguration{
        @Primary
        @Bean
        HotelUnitCaffeineCache hotelUnitCaffeineCache(HotelUnitCacheProperties hotelUnitCacheProperties, HotelUnitRedisCache hotelUnitRedisCache) {
            return new HotelUnitCaffeineCache(hotelUnitRedisCache, hotelUnitCacheProperties);
        }
    }
}

2、配置本地缓存用到的常量

@EnableConfigurationProperties注解的作用是:使使用 @ConfigurationProperties 注解的类生效。

/**
 * @author ljy
 * @date 2023/7/25
 */
@Data
@Component
@ConfigurationProperties(prefix = HotelUnitCacheProperties.LOCAL_CACHE_PREFIX)
public class HotelUnitCacheProperties implements Serializable {
    private static final long serialVersionUID = 1L;

    public static final String LOCAL_CACHE_PREFIX = "com.huwei.hotel.collector.cache.hotelunit.local-cache";

    /** 生存时间(毫秒) **/
    private Long expire;
    /** 最大的缓存条目数 **/
    private Long maximumSize;
    /** 初始化容器大小 **/
    private Integer initialCapacity;

    public HotelUnitCacheProperties(){
        super();
    }

    /** 默认的生存时间 (30天) **/
    public static final long CACHE_EXPIRE = 30L;
    /** 默认的最大缓存条目数 **/
    public static final long CACHE_MAXIMUM_SIZE = 1000L;
    /** 默认的初始化容量大小 **/
    public static final int CACHE_INITIAL_CAPACITY = 150;


    public void initCacheProperties() {
        this.expire = (expire == null ? CACHE_EXPIRE : expire);
        this.maximumSize = (maximumSize == null ? CACHE_MAXIMUM_SIZE : maximumSize);
        this.initialCapacity = (initialCapacity == null ? CACHE_INITIAL_CAPACITY : initialCapacity);
    }

3、本地缓存 初始化以及 接口实现方法。

(1)请求接口,  这个返回值 HotelUnit  可以根据 自己的业务定义返回值,我这里是用自己的业务返回值,你可以用String, Integer,对象接受,看你自己怎么存的业务。
/**
 * @author ljy
 * @date 2023/7/25
 */
@Data
public class HotelUnit implements Serializable {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "id")
    private Long id;

    @ApiModelProperty(value = "名称")
    private String name;

    @ApiModelProperty(value = "省份编码")
    private String proviceCode;

    @ApiModelProperty(value = "省份名称")
    private String proviceName;

    @ApiModelProperty(value = "缓存时间")
    private String cacheTime;
}
/**
 * @author ljy
 * @date 2023/7/25
 */
public interface HotelUnitCache {

    /**根据id查询**/
    Optional<HotelUnit> getById(Long id);
    /**获取多个**/
    List<HotelUnit> getByIds(List<Long> ids);

}
 (2)caffeine 实现类,以及初始化。

/**
 * @author ljy
 * @date 2023/7/25
 */
@Slf4j
public class HotelUnitCaffeineCache implements HotelUnitCache{

    LoadingCache<Long, HotelUnit> hotelUnitCache;

    final HotelUnitRedisCache redisCache;

    final HotelUnitCacheProperties properties;

    public HotelUnitCaffeineCache(HotelUnitRedisCache redisCache, HotelUnitCacheProperties properties){
        this.properties = properties;
        this.redisCache = redisCache;
        properties.initCacheProperties();
    }
//这个注解作用是服务启动时,加载init方法
    @PostConstruct
    public void init(){
        this.hotelUnitCache = Caffeine.newBuilder().recordStats()
                .maximumSize(properties.getMaximumSize())
                .expireAfterAccess(properties.getExpire(), TimeUnit.DAYS)
                .initialCapacity(properties.getInitialCapacity())
                .build(id -> {
                    log.info("本地缓存 - 默认酒店采集单元缓存主动加载:" + id);
                    return redisCache.getById(id).orElse(null);
                });
    }

    @Override
    public Optional<HotelUnit> getById(Long id) {
        return Optional.ofNullable(hotelUnitCache.get(id));
    }

    @Override
    public List<HotelUnit> getByIds(List<Long> ids) {
        Map<Long, HotelUnit> hotelUnitMap = hotelUnitCache.getAll(ids);
        if (CollectionUtils.isEmpty(hotelUnitMap)) {
            return new ArrayList<>();
        }
        return new ArrayList<>(hotelUnitMap.values());
    }

    /**
     * 删除单个
     * @param id
     */
    public void invalidate(Long id){
        hotelUnitCache.invalidate(id);
    }

    /**
     * 删除多个
     * @param ids
     */
    public void invalidateAll(Iterable<Long> ids){
        hotelUnitCache.invalidateAll(ids);
    }

    /**
     * 清楚所有缓存
     */
    public void invalidateAll(){
        hotelUnitCache.invalidateAll();
    }
 (3)Redis实现类
/**
 * @author ljy
 * @date 2023/7/25
 */
public class HotelUnitRedisCache implements HotelUnitCache{

    @Resource(name = RedisConfiguration.JSONRedisTemplate)
    RedisTemplate<String, Object> redisTemplate;

    /**采集管理缓存key**/
    public static final String COLLECT_UNIT_CACHE_KEY = "com.huwei.hotel.collector.cache.hotelunit";

    /**
     * 单个查询
     * @param id
     * @return
     */
    @Override
    public Optional<HotelUnit> getById(Long id) {
        HotelUnit hotelUnit = (HotelUnit) 
        redisTemplate.opsForHash().get(COLLECT_UNIT_CACHE_KEY, 
        ObjectUtils.getDisplayString(id));
       //我这里省略了查询数据库以及存储 redis的步骤,如果有需要可以在 下面加判断 ,redis中不存对象,那么去数据库DB中查询结果返回。然后顺便存数据到redis中。
       //     
        return Optional.ofNullable(hotelUnit);
    }

    /**
     * 多个查询
     * @param ids
     * @return
     */
    @Override
    public List<HotelUnit> getByIds(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return new ArrayList<>();
        }
        List<Object> list = ids.stream().map(Objects::toString).collect(Collectors.toList());
        List<Object> data = redisTemplate.opsForHash().multiGet(COLLECT_UNIT_CACHE_KEY,  list);
//我这里省略了查询数据库以及存储 redis的步骤,如果有需要可以在 下面加判断 ,redis中不存对象,那么去数据库DB中查询结果返回。然后顺便存数据到redis中。
        if (CollectionUtils.isEmpty(data)) {
            return new ArrayList<>();
        }
        return data.stream().map(info->{
            if (info instanceof HotelUnit){
                return (HotelUnit)info;
            }
            return null;
        }).filter(Objects::nonNull).collect(Collectors.toList());
    }
}
 (4)测试类 
@Api(tags = {"本地缓存API"})
@Slf4j
@RestController
public class TestHotel {
    @Autowired
    HotelUnitCache hotelUnitCache;

    @ApiOperation(value = "本地缓存")
    @PutMapping("/tests/caffeine/getById")
    public HotelUnit getById(
            @NotNull @RequestParam(value = "hotel_id") Long hotelId) {
        //根据酒店 本地缓存查省份编码
        HotelUnit hotelUnit = hotelUnitCache.getById(hotelId).orElse(null);
        return hotelUnit;
    }
}
(5)执行结果演示

1、postman

 2、首次调用会经过下图中 的1和2。第二次调用 只会进1获取对应的key,不会去2。 

 3、最后结果

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值