两级缓存(Redis + Caffeine)

问题描述 投票:0回答:2

在分析应用程序时,我们发现 Redis 正在影响执行时间,因为线程中有很多睡眠。我需要实现两级缓存或者考虑解决这个问题。

我想要两级缓存

  • L1 - 每个部署实例的本地,
  • L2 - 同一部署的所有实例的全局缓存,

我想出的解决方案是

  • 创建两个CacheManager(CaffeineCacheManager 和 RedisCacheManager),
  • 为每个缓存管理器初始化相同的缓存,
  • 使用注解@Caching和cacheable={}来使用两个缓存,

    @Caching(cacheable = {
            @Cacheable(cacheNames = CacheConfiguration.HELLO_WORLD),
            @Cacheable(cacheNames = CacheConfiguration.HELLO_WORLD, cacheManager = "cacheManagerRedis")
    })
    public String generate(String name)
    {
        log.info("  Cached method call...");
        return helloWorldService.generate(name);
    }

类的结构类似于: CachedService(此处注释)-> NonCachedService

我面临的问题

我想让它正常工作(是的 - 有效/n - 不工作):

  • [ y ] 数据被获取,然后缓存到 Redis 和本地缓存 - 这是有效的
  • [ y ] 如果本地缓存中存在数据,请勿将其移动到 Redis - 这有效
  • [ y ] 如果任何缓存包含数据,将从缓存中获取数据
  • [ n ] 如果数据存在于 Redis 中,将其移动到本地 - 这不起作用

将 @Caching 注释修改为 put={} 将值放入本地缓存会使整个缓存无法工作。


    @Caching(cacheable = {
            @Cacheable(cacheNames = CacheConfiguration.HELLO_WORLD),
            @Cacheable(cacheNames = CacheConfiguration.HELLO_WORLD, cacheManager = "cacheManagerRedis")
    }, put = {
            @CachePut(cacheNames = CacheConfiguration.HELLO_WORLD),
    })
    public String generate(String name)
    {
        log.info("  Cached method call...");
        return helloWorldService.generate(name);
    }

  • 您知道任何适用于两级缓存的 Spring-Ready 解决方案吗?
  • 我读过有关 Redis 本地缓存的内容,但这并不意味着与我的情况类似(这只是标准的 Redis 用例),
  • 我只剩下双层服务结构才能实现这个目标吗?类似于 CachedLocal -> CachedRedis -> NonCached
java spring spring-boot caching redis
2个回答
2
投票

对于其他正在寻找此功能的人,我能够实现 Ankit 建议的 CacheInterceptor。

示例:

 public class RedisMultiCacheInterceptor extends CacheInterceptor {

    @Autowired
    private CacheManager caffeineCacheManager;

    @Override
    protected Cache.ValueWrapper doGet(Cache cache, Object key) {
        //Get item from cache
        var superGetResult = super.doGet(cache, key);

        if (superGetResult == null) {
            return superGetResult;
        }

        //If retrieved from Redis, check if it's missing from caffeine on local and add it
        if (cache.getClass() == RedisCache.class) {
            var caffeineCache = caffeineCacheManager.getCache(cache.getName());

            if (caffeineCache != null) {
                caffeineCache.putIfAbsent(key, superGetResult.get());
            }
        }

        return superGetResult;
    }
}

0
投票

我尝试使用上面的拦截器。它不起作用,相反,我收到了错误“调用 init 方法失败”;嵌套异常是 java.lang.IllegalStateException:需要 'cacheOperationSources' 属性:如果没有可缓存的方法,则不要使用缓存方面。你能帮忙吗,小姐?如您所示创建类后,我是否需要在任何地方设置拦截器。

© www.soinside.com 2019 - 2024. All rights reserved.