基于Spring接口,集成Caffeine+Redis两级缓存

文章介绍了如何通过扩展Spring接口来实现基于Caffeine和Redis的两级缓存功能。首先,文章讲解了JSR107缓存规范,然后详细阐述了如何改造Spring的Cache和CacheManager接口,以及配置和使用。在分布式环境改造部分,通过Redis的消息发布/订阅功能确保各主机间一级缓存的一致性。文章提供了详细的改造步骤和测试,确保了缓存更新和删除操作在分布式环境中的正确性。
摘要由CSDN通过智能技术生成

改造

在 v3 版本中,我们使用自定义注解的方式实现了两级缓存通过 一个注解 管理的功能。本文我们换一种方式,直接通过扩展spring提供的接口来实现这个功能,在进行整合之前,我们需要简单了解一下 JSR107 缓存规范。

JSR107 规范

在 JSR107 缓存规范中定义了5个核心接口,分别是 CachingProvider , CacheManager , Cache , Entry 和 Expiry ,参考下面这张图,可以看到除了 Entry 和 Expiry 以外,从上到下都是一对多的包含关系。

从上面这张图我们可以看出,一个应用可以创建并管理多个 CachingProvider ,同样一个 CachingProvider 也可以管理多个 CacheManager ,缓存管理器 CacheManager 中则维护了多个 Cache 。

Cache 是一个类似 Map 的数据结构, Entry 就是其中存储的每一个 key-value 数据对,并且每个 Entry 都有一个过期时间 Expiry 。而我们在使用spring集成第三方的缓存时,只需要实现 Cache 和 CacheManager 这两个接口就可以了,下面分别具体来看一下。

Cache

spring中的 Cache 接口规范了缓存组件的定义,包含了缓存的各种操作,实现具体缓存操作的管理。例如我们熟悉的 RedisCache 、 EhCacheCache 等,都实现了这个接口。

在 Cache 接口中,定义了 get 、 put 、 evict 、 clear 等方法,分别对应缓存的存入、取出、删除、清空操作。不过我们这里不直接使用 Cache 接口,上面这张图中的 AbstractValueAdaptingCache 是一个抽象类,它已经实现了 Cache 接口,是spring在 Cache 接口的基础上帮助我们进行了一层封装,所以我们直接继承这个类就可以。

继承 AbstractValueAdaptingCache 抽象类后,除了创建 Cache 的构造方法外,还需要实现下面的几个方法:

// 在缓存中实际执行查找的操作,父类的get()方法会调用这个方法
protected abstract Object lookup(Object key);

// 通过key获取缓存值,如果没有找到,会调用valueLoader的call()方法
public <T> T get(Object key, Callable<T> valueLoader);

// 将数据放入缓存中
public void put(Object key, Object value);

// 删除缓存
public void evict(Object key);

// 清空缓存中所有数据
public void clear();

// 获取缓存名称,一般在CacheManager创建时指定
String getName();

// 获取实际使用的缓存
Object getNativeCache();

因为要整合 RedisTemplate 和 Caffeine 的 Cache ,所以这些都需要在缓存的构造方法中传入,除此之外构造方法中还需要再传出缓存名称 cacheName ,以及在配置文件中实际配置的一些缓存参数。先看一下构造方法的实现:

public class DoubleCache extends AbstractValueAdaptingCache {
    private String cacheName;
    private RedisTemplate<Object, Object> redisTemplate;
    private Cache<Object, Object> caffeineCache;
    private DoubleCacheConfig doubleCacheConfig;

    protected DoubleCache(boolean allowNullValues) {
        super(allowNullValues);
    }

    public DoubleCache(String cacheName,RedisTemplate<Object, Object> redisTemplate,
                       Cache<Object, Object> caffeineCache,
                       DoubleCacheConfig doubleCacheConfig){
        super(doubleCacheConfig.getAllowNull());
        this.cacheName=cacheName;
        this.redisTemplate=redisTemplate;
        this.caffeineCache=caffeineCache;
        this.doubleCacheConfig=doubleCacheConfig;
    }
    //...
}

抽象父类的构造方法中只有一个 boolean 类型的参数 allowNullValues ,表示是否允许缓存对象为 null 。除此之外, AbstractValueAdaptingCache 中还定义了两个包装方法来配合这个参数进行使用,分别是 toStoreValue 和 fromStoreValue ,特殊用途是用于在缓存 null 对象时进行包装、以及在获取时进行解析并返回。

我们之后会在 CacheManager 中调用后面这个自己实现的构造方法,来实例化 Cache 对象,参数中 DoubleCacheConfig 是使用 @ConfigurationProperties 读取的yml配置文件封装的数据对象,会在后面使用。

当一个方法添加了 @Cacheable 注解时,执行时会先调用父类 AbstractValueAdaptingCache 中的 get(key) 方法,它会再调用我们自己实现的 lookup 方法。在实际执行查找操作的 lookup 方法中,我们的逻辑仍然是先查找 Caffeine 、没有找到时再查找 Redis :</

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值