Webflux Webclient re-try with different URL

Viewed 1173

I am using webclient for the rest call and what i need is, if the primary URL is failing for the n'th time do the next re-try on Secondary URL . Please find below sample code for the logic which i am using. But it seems we cannot change the URL once he client is created and even if i change the URL its not getting effected and still requests are been fired to the initial URL.

ClientHttpConnector connector;//initiate
WebClient webClient = WebClient.builder().clientConnector(connector).build();
WebClient.RequestBodyUriSpec client = webClient.post();

client.uri("http://primaryUrl/").body(BodyInserters.fromObject("hi")).retrieve().bodyToMono(String.class).retryWhen(Retry.anyOf(Exception.class)
                    .exponentialBackoff(Duration.ofSeconds(2), Duration.ofSeconds(10)).doOnRetry(x ->
                    {
                        if (x.iteration() == 2) {
                            client.uri("http://fail_over_url/");//this does not work
                        }
                    })
                    .retryMax(2)).subscribe(WebClientTest::logCompletion, WebClientTest::handleError);

Is there any way to change the URL at the middle of re-try cycle ?

1 Answers

But it seems we cannot change the URL once he client is created

You cannot - it's immutable.

even if i change the URL its not getting effected

You're not actually changing the URL. Take a look at the uri() method - it's returning a new instance with a URI set. Since you're not doing anything with that new instance, nothing happens (as expected.)

The route I'd probably suggest is to create a separate method to form & return your basic WebClient publisher:

private Mono<String> fromUrl(String url) {
    return WebClient.builder().clientConnector(connector).build()
            .post()
            .body(BodyInserters.fromValue("hi"))
            .uri(url)
            .retrieve()
            .bodyToMono(String.class);
}

...and then do something like:

fromUrl("https://httpstat.us/400").retryWhen(Retry.backoff(2, Duration.ofSeconds(1)))
        .onErrorResume(t -> Exceptions.isRetryExhausted(t), t -> fromUrl("https://httpstat.us/500").retryWhen(Retry.backoff(5, Duration.ofSeconds(1))))
        .onErrorResume(t -> Exceptions.isRetryExhausted(t), t -> fromUrl("https://httpstat.us/200").retryWhen(Retry.backoff(7, Duration.ofSeconds(1))))

...which will try /400 3 times, then try /500 5 times, then /200 up to 7 times (but unless it's down, that will of course return on the first try.)

Note that the above example uses the latest version of reactor-core which has the retry functionality built in, rather than the retry functionality in reactor addons. Translating it to the reactor addons functionality should be reasonably straightforward.

This doesn't not strictly changing the URL in the same retry cycle, but instead chaining requests together with configurable retries per request. This then allows you to set different retry strategies on different URLs, which is advantageous if you don't necessarily want the retry to "carry on" from its previous point (It could make sense to set the backoff back to one second for a fresh URL, for example.)

Related