hba
hba

Reputation: 751

WebClient open and close connection on every element call in the Stream

Trying to understand how spring webclient handle the connections between the peers.
When using the code below :
I have as many close connection as element in the stream.
I was excpecting that when using http1.1 the reactor-netty reuse the connections. Even when trying to use the keep alive on configuration i do have the same behaviour :

[d6b705e1] Response 200 OK
[d6b705e1] Cancel signal (to close connection)

public WebClient webClient(WebClient.Builder builder) {
 return builder
  .baseUrl(config.getBaseurl())
  .clientConnector(new ReactorClientHttpConnector(
    HttpClient.create()
      .tcpConfiguration(tcpClient -> tcpClient.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, timeout))
      .secure(spec -> spec.sslContext(SslContextBuilder.forClient()))
  ))
  .build();
}

Flux<String> getIds() { ... }

Flux<Response> getResponses() {
  // here as many open and close connection as the return stream getIds 
  return getIds().flatMap(... // client.retrieve().bodyToMono(Reponse.class));
}

Upvotes: 1

Views: 10517

Answers (1)

Michael Berry
Michael Berry

Reputation: 72369

I was excpecting that when using http1.1 the reactor-netty reuse the connections.

It will, if it can. Taking the following as an example:

@RestController
public class MyController {

    private final WebClient wc;

    @Autowired
    public MyController(WebClient.Builder wcb) {
        wc = wcb
                .baseUrl("https://jsonplaceholder.typicode.com/")
                .clientConnector(new ReactorClientHttpConnector(
                        HttpClient.create()
                                .tcpConfiguration(tcpClient -> tcpClient.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 2000))
                                .secure(spec -> spec.sslContext(SslContextBuilder.forClient()))
                ))
                .build();
    }

    @GetMapping("/")
    public Mono<List<Body>> cookieSetter() {
        return Flux.range(1,10)
                .flatMap(i -> wc.get().uri("todos/" + i).retrieve().bodyToMono(Body.class))
                .collectList();
    }

    @Data
    static class Body {
        private String title;
    }

}

Load it up and you'll see a bunch of HTTP GET https://jsonplaceholder.typicode.com/todos/x and Response 200 OK, but no Cancel signal (to close connection) logs - it keeps the connection open and reuses it.

You don't provide a complete example, but it's likely there's one of two things happening here:

  • You're actively cancelling the publishers (or something in the framework is cancelling the response publishers), which then causes the connection to be closed as a side-effect of this cancellation.
  • The server that you're connecting to doesn't support keep-alive. Note that this is an optional feature, and the server doesn't have to support it, even in HTTP/1.1. In this case, it's likely returning Connection: close headers in its response. This will correctly force Netty to close the connection in between each request, rather than reusing it.

Upvotes: 0

Related Questions