0

I am running a Spring boot 3.0.1 application, using WebClient to manage HTTP requests (switching from RestTemplate). I am using WebClient in a blocking way - sending my requests like this:

webClient.get().uri(path).headers(headers).retrieve().bodyToMono(responseType).block();

Where path, headers, and responseType are already defined. Here is how I declare the WebClient Spring Bean:

@Bean
public WebClient webClient(CloseableHttpAsyncClient httpAsyncClient) {
    return WebClient.builder()
            .clientConnector(new HttpComponentsClientHttpConnector(httpAsyncClient))
            .filter(new CustomResponseHeaderFilter())
            .build();
}

Here's the implementation of the CustomResponseHeaderFilter class. (Yes I know it's not doing anything right now):

public class CustomResponseHeaderFilter implements ExchangeFilterFunction {
    @Override
    public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
        return next.exchange(request);
    }

    @Override
    public ExchangeFilterFunction andThen(ExchangeFilterFunction afterFilter) {
        return ExchangeFilterFunction.super.andThen(afterFilter);
    }

    @Override
    public ExchangeFunction apply(ExchangeFunction exchange) {
        return ExchangeFilterFunction.super.apply(exchange);
    }
}

So in the current state of my application, does the code that's run throughout an entire request run on the same thread? I've seen some information from another post here saying that the thread pauses, and in the background a separate thread is used for network operations.

More specifically, does the block() method call affect how the ExchangeFilterFunction is executed? What I mean by that is does the code in the .filter() method also run on the same thread as the request since we are using block()?

I haven't seen any documentation so far regarding blocking and the filter function, and I'm not entirely sure how this works behind the scenes.

UPDATE: This is directly relating to using MDC. Essentially I am wondering whether MDC can be used as if the entire request is run on 1 thread - like when it's used in RestTemplate. I am wondering if I add a value to the MDC context in the logResponse filter function while using .block(), will it be available to use after the request has been processed?

Thanks

Michael
  • 3,093
  • 7
  • 39
  • 83

1 Answers1

1

Typically in reactor operators continue working in the Thread on which the previous operator executed. In addition, WebClient is just a wrapper on top of underlining http clients (Netty or Apache HttpClient in your case). Http clients would maintain a separate thread pool to handle http requests.

It means that WebClient request will be executed on the caller thread but for network operations execution will be switched to httpclient-dispatch-# thread and then continue on that thread until you block.

As result request filter will be executed on the caller thread and response filter on the http client thread httpclient-dispatch-#.

Here is an example that demonstrates this

@Test
void test() {
    CloseableHttpAsyncClient httpAsyncClient = HttpAsyncClients.custom().build();
    WebClient webClient = WebClient.builder()
            .baseUrl("https://google.com")
            .clientConnector(new HttpComponentsClientHttpConnector(httpAsyncClient))
            .filter(logRequest())
            .filter(logResponse())
            .build();

    var request = webClient.get()
            .uri("/")
            .retrieve()
            .bodyToMono(String.class)
            .block();}

private ExchangeFilterFunction logRequest() {
    return (clientRequest, next) -> {
        log.info("Request: {} {}", clientRequest.method(), clientRequest.url());
        return next.exchange(clientRequest);
    };
}

private ExchangeFilterFunction logResponse() {
    return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
        log.info("Response Status: {}", clientResponse.statusCode());
        return Mono.just(clientResponse);
    });
}
21:53:54.051 [Test worker] INFO WebClientTest - START
21:53:54.728 [Test worker] INFO WebClientTest - Request: GET https://google.com/
21:53:55.445 [httpclient-dispatch-2] INFO WebClientTest - Response Status: 200 OK
21:53:55.572 [Test worker] INFO WebClientTest - END
Alex
  • 4,987
  • 1
  • 8
  • 26
  • Thanks Alex, this was mainly gathering the background information so I can figure out whether the MDC framework will work as it did using RestTemplate. I have added an UPDATE section to my question. From my understanding, and after testing and logging to see what thread the statements are being run on, it seems that MDC will not work in my scenario since the thread switches, even though `block()` is being used – Michael Feb 09 '23 at 15:33
  • 1
    If you mean using ThreadLocal to path MDC - it will not work in reactive. You would need to use reactive context. – Alex Feb 09 '23 at 15:41
  • Thank you for clearing this up! – Michael Feb 09 '23 at 15:55