【问题标题】:Reactive: Caching service response for few hours反应式:缓存服务响应几个小时
【发布时间】:2019-07-20 07:15:01
【问题描述】:

所以我的应用程序会多次调用下面昂贵的 HTTP 服务(同时由多个线程对我的应用程序的每个客户端请求具有相同和不同的 Id)。

Mono<Foo> response = myService.fetch(id);

我想将响应缓存(在内存中)几个小时,然后仅在下一个客户端请求时只调用一次来刷新缓存。

方法一:

Mono<Foo> cachedResponse = Mono.empty();

public Mono<Foo> get(String id){
   return cachedResponse.switchIfEmpty(Mono.defer(()-> 
    {
       cachedResponse = myService.fetch(id).cache(Duration.ofHours(4));
       return cachedResponse;
    }));    
}

以下方法可以吗?特别是因为多个线程可以调用具有相同 id 的 get 方法。另外,当缓存在 4 小时后失效时,是否会使 cachedResponse Mono 为空,以便 switchIfEmpty 正常工作?

方法二: 我可以使用一些缓存解决方案来存储缓存几个小时。例如

Foo getFromCacheSolution(String id);

然后,

public Mono<Foo> get(String id){
   Foo cachedFoo = getFromCacheSolution(id);
   if(cachedFoo != null){
      return Mono.just(cachedFoo);
   }
   else{
      return myService.fetch(id).doOnNext(value->storeToCacheSolution(id, value)); //line 7
   }
}

这个解决方案的问题是第 7 行会被多次调用,从而导致多次调用昂贵的 fetch 服务(例如,如果 3 个线程进入 id 为 123 的 get 方法并且 cachedFoo 为空)。使方法同步可能无济于事,因为第 7 行会立即完成。

一种解决方法是将 Mono 存储在缓存解决方案中而不是 Foo 中(不确定这是否是个好主意):

Mono<Foo> getFromCacheSolution(String id); //returns cached or empty Mono

然后,

public Mono<Foo> get(String id){
   return getFromCacheSolution(id).switchIfEmpty(Mono.defer(()-> 
    {
       cachedResponse = myService.fetch(id).doOnNext(value->storeToCacheSolution(id, value));
       return cachedResponse;
    }));
}

有什么建议或更好的选择吗?

【问题讨论】:

  • 我更喜欢第二种解决方案,但可能来自 reactor-extra 项目的caching helper 可以在这里提供帮助?

标签: java spring spring-webflux project-reactor


【解决方案1】:

您的问题由两部分组成:关于缓存和关于具有相同参数的调用的独占锁定。

  1. 缓存。

    您的第二种方法适用于内存缓存。或者,您可以使用reactor-extra 中的CacheMono

    Mono<Foo> myFoo =
            CacheMono.lookup(key -> Mono.justOrEmpty(myCache.getIfPresent(key))
                    .map(Signal::next), id)
                    .onCacheMissResume(() -> myService.fetch(id))
                    .andWriteWith((key, signal) -> Mono.fromRunnable(() ->
                            Optional.ofNullable(signal.get())
                                    .ifPresent(value -> myCache.put(key, value))));
    
  2. 具有相同参数的调用的独占锁定。

    通常我们应该避免在响应式世界中的任何锁定。但是如果你真的需要它,你的锁应该是非阻塞的。我不知道任何图书馆,但你可以在this question thread中找到一些想法和示例链接

【讨论】:

    猜你喜欢
    • 2017-07-05
    • 2019-12-27
    • 1970-01-01
    • 2017-05-30
    • 2013-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-16
    相关资源
    最近更新 更多