Кэшировать результат Mono из вызова WebClient в веб-приложении Spring WebFlux - PullRequest
0 голосов
/ 13 октября 2018

Я ищу кеширование Mono (только если оно успешно), которое является результатом вызова WebClient.

Из прочтения документации по аддонам реактора проекта я не чувствую, что CacheMono хорошо подходит, поскольку кеширует ошибки, которые я не хочу.

Таквместо использования CacheMono я делаю следующее:

Cache<MyRequestObject, Mono<MyResponseObject>> myCaffeineCache = 
    Caffeine.newBuilder()
            .maximumSize(100)
            .expireAfterWrite(Duration.ofSeconds(60))
            .build();

MyRequestObject myRequestObject = ...;

Mono<MyResponseObject> myResponseObject = myCaffeineCache.get(myRequestObject,
    requestAsKey -> WebClient.create()
                             .post()
                             .uri("http://www.example.com")
                             .syncBody(requestAsKey)
                             .retrieve()
                             .bodyToMono(MyResponseObject.class)
                             .cache()
                             .doOnError(t -> myCaffeineCache.invalidate(requestAsKey)));

Здесь я вызываю кеш на Mono и затем добавляю его в кеш кофеина.

Любые ошибки будут вводить doOnError для аннулирования кэша.

Это правильный подход к кэшированию ответа Mono WebClient?

Ответы [ 2 ]

0 голосов
/ 15 октября 2018

На самом деле, вам не нужно сохранять ошибки с CacheMono.

private Cache<MyRequestObject, MyResponseObject> myCaffeineCache;

...

Mono<MyResponseObject> myResponseObject =
        CacheMono.lookup(key -> Mono.justOrEmpty(myCaffeineCache.getIfPresent(key))
                .map(Signal::next), myRequestObject)
                .onCacheMissResume(() -> /* Your web client or other Mono here */)
                .andWriteWith((key, signal) -> Mono.fromRunnable(() ->
                        Optional.ofNullable(signal.get())
                                .ifPresent(value -> myCaffeineCache.put(key, value))));

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

0 голосов
/ 14 октября 2018

Это один из очень немногих случаев использования, когда вам действительно разрешено вызывать нереактивные библиотеки и заключать их в реактивные типы, а обработка выполнялась в операторах побочных эффектов, таких как doOnXYZ, потому что:

  • Кофеин - это кэш в памяти, поэтому, насколько я знаю, в этом нет ввода-вывода
  • Кеши часто не дают надежных гарантий относительно значений кэширования (это очень многои забудьте)

В этом случае вы можете запросить кэш, чтобы увидеть, есть ли в нем кэшированная версия (обернуть ее и вернуть сразу), и кэшировать успешный реальный ответ в операторе doOn, например так:

public class MyService {

    private WebClient client;

    private Cache<MyRequestObject, MyResponseObject> myCaffeineCache;

    public MyService() {
        this.client = WebClient.create();
        this.myCaffeineCache = Caffeine.newBuilder().maximumSize(100)
          .expireAfterWrite(Duration.ofSeconds(60)).build();
    }

    public Mono<MyResponseObject> fetchResponse(MyRequestObject request) {

        MyResponseObject cachedVersion = this.myCaffeineCache.get(myRequestObject);
        if (cachedVersion != null) {
           return Mono.just(cachedVersion);
        } else {
           return this.client.post()
                         .uri("http://www.example.com")
                         .syncBody(request.getKey())
                         .retrieve()
                         .bodyToMono(MyResponseObject.class)
                         .doOnNext(response -> this.myCaffeineCache.put(request.getKey(), response));
    }
}

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

Также вы правы насчет оператора cache, поскольку речь идет не о кэшировании значения как такового, а от воспроизведение того, что случилось с другими подписчиками.Я считаю, что операторы cache и replay на самом деле являются синонимами для Flux.

...