Реактив: кеширование ответа службы на несколько часов - PullRequest
0 голосов
/ 26 февраля 2019

Таким образом, мое приложение будет вызывать ниже дорогой HTTP-сервис несколько раз (одновременно несколькими потоками с одинаковыми, а также разными идентификаторами для каждого запроса клиента к моему приложению).

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

Я хотел бы кэшироватьОтвет (в памяти) в течение нескольких часов, а затем только при следующем запросе клиента сделать только один вызов для обновления кэша.

Подход 1:

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;
    }));    
}

подходит ли следующий подход?В частности, поскольку несколько потоков могут вызывать метод get с одинаковым идентификатором.Кроме того, если кэш-память через 4 часа станет недействительной, сделает ли cachedResponse Mono пустым для правильной работы switchIfEmpty?

Подход 2: Я мог бы использовать какое-то решение для кэширования для хранения кеша в течение нескольких часов.,например,

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 будет вызываться несколько раз, что приведет к многочисленным вызовам дорогостоящей службы выборки (например, если 3 потокавведите метод get с идентификатором 123 и cachedFoo имеет значение null).Выполнение синхронизированного метода может не помочь, так как строка 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;
    }));
}

Любые рекомендации или лучшие альтернативы?

1 Ответ

0 голосов
/ 27 февраля 2019

Ваш вопрос состоит из двух частей: о кэшировании и об эксклюзивной блокировке вызовов с одинаковыми параметрами.

  1. Кэширование.

    Ваш второй подход хорош длякеш памяти.В качестве альтернативы вы можете использовать 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. Эксклюзивная блокировка для вызовов с такими же параметрами.

    Обычно мы должныизбегать любых замков в реактивном мире.Но если вам это действительно нужно, ваш замок должен быть неблокирующим.Я не знаю ни одной библиотеки, но вы можете найти некоторые идеи и ссылки с примерами в этой ветке вопросов

...