Integrating RestTemplate with an OkHttp Connection Pool in Spring Boot
OkHttp provides an efficient HTTP stack for RestTemplate by reusing sockets across requests to the same host, pooling idle connections to cut latency, transparently compressing responses with GZIP, and retrying recoverable network failures. With TLS support (including SNI/ALPN) and IP failover on connection issues, it’s a strong alterantive to the JDK HTTP client for production workloads.
Dependencies
Maven coordinates for OkHttp (Spring Boot’s spring-boot-starter-web already brings RestTemplate):
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
Spring configuration (OkHttp client, connection pool, and RestTemplate)
package com.example.http;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;
@Configuration
public class HttpClientConfig {
// Reuse idle connections for up to 5 minutes, up to 200 idle sockets
@Bean
public ConnectionPool httpConnectionPool() {
return new ConnectionPool(200, 5, TimeUnit.MINUTES);
}
@Bean
public OkHttpClient okHttpClient(ConnectionPool httpConnectionPool) {
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.connectionPool(httpConnectionPool)
.retryOnConnectionFailure(true)
.followRedirects(true)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS);
// If you need to trust all certificates (NOT for production),
// uncomment the following two lines and also declare the beans below.
// builder.sslSocketFactory(insecureSslSocketFactory(), insecureTrustManager());
// builder.hostnameVerifier((host, session) -> true);
return builder.build();
}
@Bean
public RestTemplate restTemplate(OkHttpClient okHttpClient) {
return new RestTemplate(new OkHttp3ClientHttpRequestFactory(okHttpClient));
}
// --- Optional: trust-all SSL (use only for testing) ---
@Bean
public X509TrustManager insecureTrustManager() {
return new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {
// no-op
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {
// no-op
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
};
}
@Bean
public SSLSocketFactory insecureSslSocketFactory() {
try {
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, new TrustManager[]{insecureTrustManager()}, new SecureRandom());
return ctx.getSocketFactory();
} catch (NoSuchAlgorithmException | KeyManagementException e) {
throw new IllegalStateException("Unable to create insecure SSL context", e);
}
}
}
Example usage with RestTemplate
package com.example.http;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;
@Service
public class RemoteApiClient {
private final RestTemplate restTemplate;
public RemoteApiClient(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public String getUser(String userId) {
String url = "https://httpbin.org/get?userId={id}";
return restTemplate.getForObject(url, String.class, userId);
}
public String postPayload(Map<String, Object> payload) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(payload, headers);
ResponseEntity<String> response = restTemplate.postForEntity("https://httpbin.org/post", entity, String.class);
return response.getBody();
}
// Simple test invocation
public static void main(String[] args) {
// In a real application, Spring Boot will create beans and wire them.
RestTemplate rt = new RestTemplate();
RemoteApiClient client = new RemoteApiClient(rt);
Map<String, Object> body = new HashMap<>();
body.put("name", "alice");
body.put("age", 30);
System.out.println(client.getUser("123"));
System.out.println(client.postPayload(body));
}
}
Notes on tuning
- Increase maxIdleConnections in ConnectionPool if you expect many distinct hosts or high fan-out, and extend keepAliveDuration for servers with long-lived keep-alives.
- Consider setting a global call timeout if you need an overall per-request deadline: client.newBuilder().callTimeout(60, TimeUnit.SECONDS).
- OkHttp enables transparent GZIP by default; avoid adding manual compression headers unless required by the server.
- For production TLS, use system/default trust stores or a pinned certificate strategy instead of the trust-all example.