Conditional repeat or retry on Mono with webclient from Spring WebFlux

Bernado :

What i want to do is a conditional repeat on a Mono in Webflux with webclient.The Situation is the following:

We have some business rest service service that returns a generated document. the generation of this document is triggered from another service that gets called before this one. But, back to business: the document generation service needs from 10-30 seconds. What we want to do is: check after 10 seconds if document (Mono) is generated. If so, all is fine. If not, repeat (or retry) after another 5 seconds and check if document is generated. And so on until (worst case) a timeout after 30 seconds. Is this possible? Some (pseudo) code:

return this.webClient.post().uri(SERVICE_URL)).        
body(BodyInserters.fromObject(docRequest)).retrieve().
bodyToMono(Document.class).
delaySubscription(Duration.ofSeconds(10)).
repeat5TimesWithDynamicTimeDelayUntil(!document.isEmpty()).
subscribe();

Greetz Bernardo

Phil Clay :

Yes, it is possible.

Mono has two concepts for re-subscribing (and thus, re-triggering the request)

  • retry = re-subscribe if the upstream completed with an exception
  • repeat = re-subscribe if the upstream completed successfully

Each concept has multiple overloaded methods on Mono for different use cases. Look for the retry* and repeat* methods. For example, a common use case is retrying with an exponential backoff strategy via retryBackoff.

More complex use cases are supported via retryWhen and repeatWhen. The reactor-extras project includes some builders to help you build the Functions to pass to those methods.

Here is an example that retries if the mono completed with an exception a maximum of 5 times with 5 seconds between each attempt

this.webClient
        .post()
        .uri(SERVICE_URL)
        .body(BodyInserters.fromObject(docRequest))
        .retrieve()
        .bodyToMono(Document.class)
        .retryWhen(Retry.any()
                .fixedBackoff(Duration.ofSeconds(5))
                .retryMax(5))
        .delaySubscription(Duration.ofSeconds(10))

There are other Backoff strategies (e.g. exponential), and other options available to fully customize retries (e.g. using a timeout instead of max number of retries).

If you need to repeat on success, then use .repeatWhen(Repeat...) or .repeatWhenEmpty(Repeat...) instead of .retryWhen(Retry...) above. For example:

this.webClient
        .post()
        .uri(SERVICE_URL)
        .body(BodyInserters.fromObject(docRequest))
        .retrieve()
        .bodyToMono(Document.class)
        .filter(document -> !document.isEmpty())
        .repeatWhenEmpty(Repeat.onlyIf(repeatContext -> true)
                .exponentialBackoff(Duration.ofSeconds(5), Duration.ofSeconds(10))
                .timeout(Duration.ofSeconds(30)))
        .delaySubscription(Duration.ofSeconds(10))

You can also chain a .retry* with a .repeat* if you want to re-subscribe on both success or failure.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=68966&siteId=1