[Spring Boot] How to solve WebClient Connection reset by peer error

Jay Kim
2 min readJul 22, 2023

--

I had a requirement to fetch user data from an external system. It was implemented using WebClient as part of declarative http client. Interestingly, the very first request after opening the application on a new browser tab returned Connection reset by peer error.

This issue was solved by disabling keep-alive for outgoing requests.

Before:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.support.WebClientAdapter;
import org.springframework.web.service.invoker.HttpServiceProxyFactory;


@Configuration
public class HttpProxyConfiguration {

@Value("${tracker.url}")
private String trackerUrl;

@Bean
TrackerClient trackerClient(WebClient.Builder builder) {
var wc = builder.baseUrl(trackerUrl)
.build();

var wca = WebClientAdapter.forClient(wc);
return HttpServiceProxyFactory.builder()
.clientAdapter(wca)
.build()
.createClient(TrackerClient.class);
}
}

After:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.support.WebClientAdapter;
import org.springframework.web.service.invoker.HttpServiceProxyFactory;
import reactor.netty.http.client.HttpClient;


@Configuration
public class HttpProxyConfiguration {

@Value("${tracker.url}")
private String trackerUrl;

@Bean
TrackerClient trackerClient(WebClient.Builder builder) {
var httpClient = HttpClient.newConnection().keepAlive(false);
var reactorClientHttpConnector = new ReactorClientHttpConnector(httpClient);

var wc = builder.baseUrl(trackerUrl)
.clientConnector(reactorClientHttpConnector)
.build();

var wca = WebClientAdapter.forClient(wc);
return HttpServiceProxyFactory.builder()
.clientAdapter(wca)
.build()
.createClient(TrackerClient.class);
}

Update (2023–09–29)

After looking this github issue, it seemed that disabling connection pool or disabling keep alive is not recommended. So I applied the solution mentioned in this github issue to set up some connection timeouts.

@Configuration
public class HttpProxyConfiguration {

@Value("${tracker.url}")
private String trackerUrl;

@Bean
TrackerClient trackerClient(WebClient.Builder builder) {
ConnectionProvider provider = ConnectionProvider.builder("fixed")
.maxConnections(500)
.maxIdleTime(Duration.ofSeconds(20))
.maxLifeTime(Duration.ofSeconds(60))
.pendingAcquireTimeout(Duration.ofSeconds(60))
.evictInBackground(Duration.ofSeconds(120)).build();

HttpClient httpClient = HttpClient.create(provider);
httpClient.warmup().block();

var reactorClientHttpConnector = new ReactorClientHttpConnector(httpClient);

var wc = builder.baseUrl(trackerUrl)
.clientConnector(reactorClientHttpConnector)
.build();

var wca = WebClientAdapter.forClient(wc);
return HttpServiceProxyFactory.builder()
.clientAdapter(wca)
.build()
.createClient(TrackerClient.class);
}
}

The code mentioned above has been in production for at least two weeks now. So far I haven’t encountered any errors yet. By setting up the connection pool, I had hoped to improve the application’s performance, particularly because it often makes numerous requests to another service to acquire all necessary data for the frontend. I plan to continue monitoring this solution over time to assess its impact and determine whether it indeed enhances the application’s performance.

--

--