使用 spring webflux 替代 @Cacheable

Posted

技术标签:

【中文标题】使用 spring webflux 替代 @Cacheable【英文标题】:Alternatives to @Cacheable with spring webflux 【发布时间】:2019-03-23 02:38:51 【问题描述】:

我需要缓存来自ReactiveMongoRepository 的数据。数据大约每年更新两次,所以我不关心缓存是否过期。

由于我们can't use @Cacheable 使用通量,我想找到一种直接、简单的方法来将来自 Mongo 的数据存储到 redis,并使用该数据(如果存在),否则存储它并提供原始数据。

有没有比这样做更直接的方法

  @GetMapping
  public Flux<AvailableInspection> getAvailableInspectionsRedis() 
    AtomicInteger ad = new AtomicInteger();
    return availableInspectionReactiveRedisOperations.opsForZSet().range("availableInspections", Range.<Long>from(Range.Bound.inclusive(0L)).to(Range.Bound.inclusive(-1L)))
        .switchIfEmpty(availableInspectionMongoRepository.findAll().map(e -> 
          availableInspectionReactiveRedisOperations.opsForZSet().add("availableInspections", e, ad.getAndIncrement()).block();
          return e;
        ));
  

我正在寻找的是一个选项,它可以让我缓存数据,就像 @Cacheable 批注一样。我正在寻找一种能够缓存任何类型通量的通用解决方案。

【问题讨论】:

【参考方案1】:

我怀疑这个问题是否有现成的解决方案。 但是,您可以轻松构建自己的接口来获取通用缓存对象并将它们加载到缓存中:

public interface GetCachedOrLoad<T> 

  Flux<T> getCachedOrLoad(String key, Flux<T> loader, Class<? extends T> clazz);

每个需要此功能的类将通过构造函数注入它并按如下方式使用它:

public class PersistedObjectRepository 

  private final GetCachedOrLoad<PersistedObject> getCachedOrLoad;

  public PersistedObjectRepository(final GetCachedOrLoad<PersistedObject> getCachedOrLoad) 
    this.getCachedOrLoad = getCachedOrLoad;
  

  public Flux<PersistedObject> queryPersistedObject(final String key) 
    return getCachedOrLoad.getCachedOrLoad(key, queryMongoDB(key), PersistedObject.class);
  

  private Flux<PersistedObject> queryMongoDB(String key) 
    // use reactivemongo api to retrieve Flux<PersistedObject>
  

然后您需要创建一个实现GetCachedOrLoad&lt;T&gt; 的对象并使其可用于依赖注入。

public class RedisCache<T> implements GetCachedOrLoad<T> 

  private final Function<String, Flux<String>> getFromCache;
  private final BiConsumer<String, String> loadToCache;
  private final Gson gson;

  public RedisCache(Gson gson, RedisReactiveCommands<String, String> redisCommands) 
    this.getFromCache = key -> redisCommands.lrange(key, 0, -1);
    this.loadToCache = redisCommands::lpush;
    this.gson = gson;
  

  @Override
  public Flux<T> getCachedOrLoad(final String key, Flux<T> loader, Class<? extends T> clazz) 
    final Flux<T> cacheResults = getFromCache.apply(key)
      .map(json -> gson.fromJson(json, clazz));
    return cacheResults.switchIfEmpty(
      loader.doOnNext(value -> loadToCache.accept(key, gson.toJson(value))));
  

希望这足够通用:) . PS。这不是一个生产就绪的实现,需要根据您自己的需求进行调整,例如添加异常处理、自定义 json 序列化等等。

【讨论】:

以上是关于使用 spring webflux 替代 @Cacheable的主要内容,如果未能解决你的问题,请参考以下文章

只会SpringMVC了,太Low了!Spring又官宣了一个更牛逼的替代框架!

只会SpringMVC了,太Low了!Spring又官宣了一个更牛逼的替代框架

求求你别只会SpringMVC了,太Low了!Spring又官宣了一个更牛逼的替代框架!

Spring-WebFlux使用,一文带你从0开始学明白Spring-WebFlux,学明白响应式编程

Spring @Async 与 Spring WebFlux

技术使用 Spring 5 的 WebFlux 开发反应式 Web 应用