
Apache HttpClient Example: CloseableHttpClient Usage
Apache HttpClient’s CloseableHttpClient represents the modern, thread-safe approach to handling HTTP requests in Java applications. This powerful component provides fine-grained control over connection management, request configuration, and resource cleanup while offering superior performance characteristics compared to legacy HTTP libraries. Throughout this guide, you’ll master the implementation patterns, configuration options, and troubleshooting techniques that separate production-ready code from basic tutorials.
How CloseableHttpClient Works
CloseableHttpClient operates on a request-response model built around connection pooling and resource management. Unlike the deprecated DefaultHttpClient, it implements the Closeable interface, ensuring proper resource cleanup through try-with-resources blocks or explicit close() calls.
The architecture centers on several key components:
- HttpClientBuilder – Factory for creating configured client instances
- RequestConfig – Per-request timeout and behavior settings
- PoolingHttpClientConnectionManager – Connection pool management
- HttpRequestBase – Request method implementations (GET, POST, PUT, etc.)
Connection pooling significantly reduces overhead by reusing established connections. The default pool maintains 2 connections per route and 20 total connections, though production applications typically require custom configuration.
Basic Implementation Setup
Let’s start with a minimal working example that demonstrates proper resource management:
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
public class BasicHttpClientExample {
public static void main(String[] args) {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpGet request = new HttpGet("https://httpbin.org/get");
try (CloseableHttpResponse response = httpClient.execute(request)) {
System.out.println("Status: " + response.getStatusLine().getStatusCode());
String responseBody = EntityUtils.toString(response.getEntity());
System.out.println("Response: " + responseBody);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
For Maven projects, add this dependency to your pom.xml:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.14</version>
</dependency>
The try-with-resources pattern automatically closes both the client and response, preventing connection leaks that plague many production applications.
Advanced Configuration and Connection Management
Production environments demand sophisticated configuration. Here’s a robust setup suitable for high-throughput applications:
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
public class ProductionHttpClient {
private static CloseableHttpClient createHttpClient() {
// Configure connection pool
PoolingHttpClientConnectionManager connectionManager =
new PoolingHttpClientConnectionManager();
connectionManager.setMaxTotal(100);
connectionManager.setDefaultMaxPerRoute(20);
// Configure request timeouts
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(5000)
.setSocketTimeout(10000)
.setConnectionRequestTimeout(3000)
.build();
return HttpClientBuilder.create()
.setConnectionManager(connectionManager)
.setDefaultRequestConfig(requestConfig)
.setUserAgent("MyApp/1.0")
.build();
}
}
Key configuration parameters explained:
Parameter | Purpose | Recommended Value |
---|---|---|
MaxTotal | Maximum connections in pool | 50-200 depending on load |
DefaultMaxPerRoute | Max connections per destination | 10-50 per target server |
ConnectTimeout | Time to establish connection | 3000-5000ms |
SocketTimeout | Max time between data packets | 10000-30000ms |
ConnectionRequestTimeout | Time to get connection from pool | 1000-3000ms |
Real-World Examples and Use Cases
Let’s examine practical scenarios you’ll encounter in production systems.
POST Request with JSON Payload
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.ContentType;
public void sendJsonData(CloseableHttpClient httpClient, String jsonData) {
HttpPost post = new HttpPost("https://api.example.com/data");
try {
StringEntity entity = new StringEntity(jsonData, ContentType.APPLICATION_JSON);
post.setEntity(entity);
post.setHeader("Authorization", "Bearer " + getAccessToken());
try (CloseableHttpResponse response = httpClient.execute(post)) {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode >= 200 && statusCode < 300) {
System.out.println("Success: " + EntityUtils.toString(response.getEntity()));
} else {
System.err.println("HTTP Error: " + statusCode);
}
}
} catch (Exception e) {
System.err.println("Request failed: " + e.getMessage());
}
}
File Upload with Multipart
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
public void uploadFile(CloseableHttpClient httpClient, File file) {
HttpPost post = new HttpPost("https://upload.example.com/files");
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addPart("file", new FileBody(file));
builder.addTextBody("description", "Uploaded via HttpClient");
post.setEntity(builder.build());
try (CloseableHttpResponse response = httpClient.execute(post)) {
System.out.println("Upload status: " + response.getStatusLine().getStatusCode());
} catch (Exception e) {
e.printStackTrace();
}
}
Comparison with Alternative HTTP Clients
Feature | Apache HttpClient | OkHttp | Java 11 HttpClient |
---|---|---|---|
Connection Pooling | Excellent, highly configurable | Automatic, good defaults | Built-in, limited config |
Async Support | Limited (HttpAsyncClient) | Native async/await | CompletableFuture based |
HTTP/2 Support | Via HttpClient 5.x | Full support | Native support |
Memory Footprint | Larger (multiple JARs) | Moderate | Built into JDK |
Learning Curve | Steep but powerful | Moderate | Simple for basic use |
Apache HttpClient excels in enterprise environments requiring fine-grained control over connection behavior, extensive authentication schemes, and complex request/response processing. For microservices deployed on VPS infrastructure, the connection pooling capabilities provide significant performance advantages under load.
Best Practices and Common Pitfalls
Resource Management Best Practices
- Always use try-with-resources - Prevents connection leaks that can exhaust server resources
- Reuse HttpClient instances - Creating new instances is expensive and defeats connection pooling
- Configure appropriate timeouts - Prevents threads from hanging indefinitely
- Monitor connection pool metrics - Track pool utilization in production
Common Pitfalls to Avoid
// WRONG: Creates new client for each request
public void badExample(String url) {
try (CloseableHttpClient client = HttpClients.createDefault()) {
HttpGet get = new HttpGet(url);
// This defeats connection pooling entirely
}
}
// CORRECT: Reuse client instance
public class HttpService {
private final CloseableHttpClient httpClient;
public HttpService() {
this.httpClient = HttpClients.createDefault();
}
public void makeRequest(String url) {
HttpGet get = new HttpGet(url);
try (CloseableHttpResponse response = httpClient.execute(get)) {
// Process response
}
}
// Don't forget cleanup on service shutdown
public void shutdown() throws IOException {
httpClient.close();
}
}
Performance Optimization
For applications running on dedicated servers with high concurrent loads, consider these optimizations:
// Optimize for high-throughput scenarios
PoolingHttpClientConnectionManager connectionManager =
new PoolingHttpClientConnectionManager();
// Increase pool sizes for high-concurrency applications
connectionManager.setMaxTotal(200);
connectionManager.setDefaultMaxPerRoute(50);
// Set shorter idle timeouts to free up connections
connectionManager.closeIdleConnections(30, TimeUnit.SECONDS);
// Enable TCP keep-alive at OS level
System.setProperty("http.keepAlive", "true");
System.setProperty("http.maxConnections", "50");
Troubleshooting Common Issues
Connection Pool Exhaustion
When you see "Connection pool shut down" or timeout exceptions, implement connection monitoring:
public void monitorConnectionPool(PoolingHttpClientConnectionManager cm) {
System.out.println("Max connections: " + cm.getMaxTotal());
System.out.println("Leased connections: " + cm.getTotalStats().getLeased());
System.out.println("Available connections: " + cm.getTotalStats().getAvailable());
// Force cleanup of stale connections
cm.closeExpiredConnections();
cm.closeIdleConnections(30, TimeUnit.SECONDS);
}
SSL/TLS Configuration
For secure communications, especially with self-signed certificates in development:
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
// WARNING: Only for development - bypasses certificate validation
SSLContext sslContext = SSLContextBuilder.create()
.loadTrustMaterial(new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) {
return true; // Trust all certificates
}
})
.build();
CloseableHttpClient httpClient = HttpClientBuilder.create()
.setSSLContext(sslContext)
.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
.build();
The Apache HttpClient documentation provides comprehensive coverage of advanced features: Apache HttpComponents Documentation.
Mastering CloseableHttpClient requires understanding both its power and responsibility. Proper configuration, resource management, and monitoring separate robust production applications from brittle prototypes. The investment in learning these patterns pays dividends in application reliability and performance scalability.

This article incorporates information and material from various online sources. We acknowledge and appreciate the work of all original authors, publishers, and websites. While every effort has been made to appropriately credit the source material, any unintentional oversight or omission does not constitute a copyright infringement. All trademarks, logos, and images mentioned are the property of their respective owners. If you believe that any content used in this article infringes upon your copyright, please contact us immediately for review and prompt action.
This article is intended for informational and educational purposes only and does not infringe on the rights of the copyright owners. If any copyrighted material has been used without proper credit or in violation of copyright laws, it is unintentional and we will rectify it promptly upon notification. Please note that the republishing, redistribution, or reproduction of part or all of the contents in any form is prohibited without express written permission from the author and website owner. For permissions or further inquiries, please contact us.