The Java 11 HttpClient API provides a lot of configuration options that you can use to tune its performance. In this tutorial, we will explore some of the most important ones.
Connection Pooling
Connection pooling is a technique that allows you to reuse existing connections instead of creating new ones for each request. This can improve the performance of your HTTP client by reducing the overhead of establishing new connections.
The jdk.httpclient.connectionPoolSize
system property is an option that can be used to control the size of the connection pool used by the Java 11 HttpClient
API. When initializing an instance of HttpClient
, you can set this system property to limit the maximum number of concurrent connections that can be opened.
By default, this property is set to 0, which means that the connection pool size is unlimited. This can result in high resource usage and may cause performance issues if too many connections are opened at the same time.
To limit the number of connections, you can set the value of this property to an integer greater than 0. For example, you can set the connection pool size to 10 by setting the system property as follows:
System.setProperty("jdk.httpclient.connectionPoolSize", "10");
When this property is set, the Java HttpClient
API will limit the number of connections to the specified value. If there are more requests than the available connections, the requests will be queued and executed when a connection becomes available. This can help to prevent resource exhaustion and improve the overall performance of your HTTP client.
Thread Pooling
By default, the Java 11 HttpClient API uses a shared thread pool to handle requests. However, you can also create your own thread pool and use it to execute requests. This can be useful if you want more fine-grained control over the concurrency of your HTTP client. To use a custom thread pool, you can pass an instance of Executor
to the HttpClient.newBuilder()
method.
import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolExample { public static void main(String[] args) throws Exception { ExecutorService executor = Executors.newFixedThreadPool(10); HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) .executor(executor) .build(); URI uri = new URI("https://httpbin.org/get"); HttpRequest request = HttpRequest.newBuilder() .uri(uri) .GET() .build(); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); executor.shutdown(); } }
In the above example, we create a new fixed thread pool with 10 threads using Executors.newFixedThreadPool(10)
and pass it to the HttpClient.newBuilder().executor(Executor)
method.
Compression
HTTP compression is a technique that reduces the size of the response body by compressing it before sending it over the network. This can significantly improve the performance of your HTTP client, especially when dealing with large response bodies. The Java 11 HttpClient API supports gzip compression by default, but you can also enable other compression formats like deflate and brotli using the HttpRequest.Builder.header(String, String)
method.
import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class CompressionExample { public static void main(String[] args) throws Exception { HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) .build(); URI uri = new URI("https://httpbin.org/gzip"); HttpRequest request = HttpRequest.newBuilder() .uri(uri) .header("Accept-Encoding", "gzip, deflate, br") .GET() .build(); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } }
In the above example, we set the Accept-Encoding
header to include gzip, deflate, and brotli compression formats using the header(String, String)
method.
You should be able to see the Header settings in the Response Body:
Timeout Configuration
Setting appropriate timeouts for HTTP requests is crucial for avoiding performance issues caused by long running requests. The Java 11 HttpClient API allows you to configure connection timeout and read timeout using the HttpClient.newBuilder()
method and the HttpRequest.Builder.timeout(Duration)
method, respectively.
import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.time.Duration; public class TimeoutConfigurationExample { public static void main(String[] args) throws Exception { HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) .connectTimeout(Duration.ofSeconds(5)) .build(); URI uri = new URI("https://httpbin.org/delay/5"); HttpRequest request = HttpRequest.newBuilder() .uri(uri) .GET() .timeout(Duration.ofSeconds(2)) .build(); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } }
Conclusion
In this tutorial, we explored some of the ways to tune the performance of the Java 11 HttpClient API, including connection pooling, timeout configuration, compression, and thread pooling. If you want to compare the performance tuning of Java 11 HttpClient API with Apache HTTP Client, then check this article: Writing high performance Apache HTTP Clients