怎么使用caffeine_redis自定义二级缓存

    问题

    基于提出的需求,我认为主要有以下两个问题:

    • 因为有本地缓存,如何保证数据一致性。当一个节点数据改变,其他节点的数据如何失效?

    • 数据不对,需要重新同步,缓存如何失效?

    流程图

    提高系统性能,轻松使用caffeine_redis实现自定义二级缓存

    接下来就是配合产品和其他开发人员画出流程图,如下:

    • 使用一张配置表,记录是否需要缓存,是否开启缓存,来达到通知时候缓存失效的情况。

    • 因为项目要求一般,即使消息丢失,也不会存在太大的影响,所以最终选择了 redis 里面的订阅、发布功能,实现通知其他节点失效本地缓存。

    开发

    上面问题清楚了,流程图也清楚了。那就准备开始写 bug 了。整体思路是自定义注解实现切面,尽量降低对业务代码的耦合度。

    CacheConfig

    主要是在代码中解释,定义一个 CacheManager 以便与业务结合。需要特别注意最大可缓存条数,以免占用程序内存过多导致内存爆满。当然也不能太小了,因为还要考虑命中率的问题。所以这就得结合实际得业务来确定最终的大小。

    @Bean(name = JKDDCX)
    @Primary
    public CacheManager cacheManager() {
    CaffeineCacheManager cacheManager = new CaffeineCacheManager();

    cacheManager.setCaffeine(Caffeine.newBuilder()
    // 设置最后一次写入或访问后经过固定时间过期
    .expireAfterAccess(EXPIRE, TIME_UNIT)
    //设置本地缓存写入后过期时间
    .expireAfterWrite(EXPIRE, TIME_UNIT)
    // 初始的缓存空间大小
    .initialCapacity(500)
    // 缓存的最大条数
    .maximumSize(1000));
    // 使用人数 * 5 (每个人不同的入参 5 条)\
    return cacheManager;

    } @CaffeineCache

    自定义注解,把可以用到的参数都能加上。

    @Target({ ElementType.METHOD ,ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface CaffeineCache {

    public String moudleId() default "
    "
    ;


    //用于在数据库中配置参数
    public String methodId() default "
    "
    ;

    public String cachaName() default "
    "
    ;


    //动态切换实际的 CacheManager
    public String cacheManager() default "
    "
    ;


    } CacheMessageListener

    缓存监听器,主要是保证多节点数据一致性的问题。当一个节点缓存更新,通知其他的节点相应处理。使用 Redis 的发布订阅功能,并实现MessageListener 接口来实现主要技术。

    当然下面还有个细节就是一般生产环境是禁用 Redis#keys 命令的,所以得换个方式扫描对应的 key。

    public class CacheMessageListener implements MessageListener {
    @Override
    public void onMessage(Message message, byte[] pattern) {
    CacheMessage cacheMessage = (CacheMessage) redisTemplate.getValueSerializer().deserialize(message.getBody());

    logger.info("
    收到redis清除缓存消息, 开始清除本地缓存, the cacheName is {}, the key is {}"
    , cacheMessage.getCacheName(), cacheMessage.getKey());

    // redisCaffeineCacheManager.clearLocal(cacheMessage.getCacheName(), cacheMessage.getKey());


    /**
    * 如果是一个类上使用了 注解 @CaffeineCache ,那么所有接口都会缓存。
    * 下面的逻辑是:除了当前模块的接口访问的入参 key,其他的 redis 缓存都会被清除
    * (比如此模块的表更新了,但是当前调用此接口只是缓存了当前这个入参的redis,其他的数据删除)
    */
    String prefixKey = RedisConstant.WXYMG_DATA_CACHE + cacheMessage.getCacheName();

    Set<
    String>
    keys = redisTemplate.execute((RedisCallback<
    Set<
    String>
    >
    ) connection ->
    {
    Set<
    String>
    keysTmp = new HashSet<
    >
    ();

    Cursor<
    byte[]>
    cursor = connection.scan(new ScanOptions.ScanOptionsBuilder().
    match(prefixKey + "
    *"
    ).
    count(50).build());

    while (cursor.hasNext()) {
    keysTmp.add(new String(cursor.next()));

    }
    return keysTmp;

    });

    Iterator iterator = keys.iterator();

    while (iterator.hasNext()) {
    if (iterator.next().toString().equals(cacheMessage.getKey())) {
    iterator.remove();

    }
    }
    redisTemplate.delete(keys);


    cacheConfig.cacheManager().getCache(cacheMessage.getCacheName()).clear();
    //cacheName 下的都删除
    }
    } CaffeineCacheAspect

    然后就是切面的逻辑处理,里面的内容和 流程图 一模一样,只是使用代码实现了需求。

    其中:下面的代码是 Redis 发布消息。

    redisTemplate.convertAndSend(CacheConfig.TOPIC, new CacheMessage(caffeineCache.cachaName(), redisKey));
    CacheMessage

    这是在 Redis 发布消息的时候一个消息体,也是自定义的,可以加更多的参数属性

    public class CacheMessage implements Serializable {

    private static final long serialVersionUID = -1L;


    private String cacheName;


    private Object key;


    public CacheMessage(String cacheName, Object key) {
    super();

    this.cacheName = cacheName;

    this.key = key;

    }

    }

    1. 什么是二级缓存
    二级缓存指在原有缓存基础之上再次缓存,常用于解决缓存雪崩问题。Caffeine_redis是一款功能强大且易于使用的缓存框架,支持自定义二级缓存。
    2. 自定义二级缓存的优势
    Caffeine_redis的自定义二级缓存可以将数据同时存储到本地缓存和远程缓存中,提高系统读写效率。同时,在出现缓存穿透时,本地缓存能够有效避免对后端服务造成过大的负担。
    3. 如何集成Caffeine_redis
    需要在项目中添加Caffeine和Redis的依赖,然后使用Spring集成Caffeine_redis即可。
    4. 如何使用自定义二级缓存
    将自定义的缓存实现类注入到Caffeine_redis的构建器中,然后在缓存命名空间中指定二级缓存的名称和过期时间即可。
    5. 如何有效管理二级缓存
    为了更好地管理二级缓存,我们可以设置多个Redis节点,同时使用Redis Sentinel来实现高可用性。
    6. 如何处理缓存的更新问题
    当缓存数据被更新时,应该立即刷新本地缓存和Redis缓存,以确保所有节点都有最新的数据。
    7. 总结
    Caffeine_redis是一款值得推荐的缓存框架,有效解决了缓存雪崩和缓存穿透问题。使用自定义二级缓存,可以大大提高系统读写效率,是实现系统性能优化的重要手段之一。