I am trying out Apache HTTPClient 4.3.6 connection pool manager to increase the throughput of my HTTP calls. My assumption is, HTTPClient implementation in general is using persistence connection. The result of my test code (included at the end), however, showed that multiple concurrent HTTP connections using JDK URLConnection perform better.
- How do I make
HTTPClientfast? - Does
HTTPClientuses the same HTTP connection forhttp://localhost:9000/user/123andhttp://localhost:9000/user/456?
Thanks
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
public class FooTest {
public static void main(String[] args) throws Exception {
runWithConnectionPool();
}
private static String extract(BufferedReader reader) throws Exception {
StringBuilder buffer = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
buffer.append(line);
}
return buffer.toString();
}
private static void runWithConnectionPool() throws Exception {
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(1);
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(cm)
.setMaxConnTotal(100)
.setMaxConnPerRoute(100)
.build();
long start = System.currentTimeMillis();
HttpGet getReq = new HttpGet("http://www.google.com");
/*
Option A: Using HTTP connection pool
Option B: Individual JDK 8 URL connection
*/
// Thread[] workers = generateAndStart(10, httpClient, getReq, 0); // (A)
Thread[] workers = generateAndStart(10, getReq.getURI().toURL(), 0); // (B)
for (int i = 0; i < workers.length; i++) {
workers[i].join();
}
System.out.println("Elasped: " + (System.currentTimeMillis() - start));
}
private static Thread[] generateAndStart(int num, URL url, long delay) {
Thread[] workers = new Thread[num];
for (int i = 0; i < num; i++) {
System.out.println("Starting worker: " + i);
int j = i;
workers[i] = new Thread(() -> connect(url, delay, j));
workers[i].start();
}
return workers;
}
private static void connect(URL url, long delay, int ndx) {
try {
System.out.println(url.toURI().toString() + " started.");
} catch (Exception e) {
e.printStackTrace();
}
try {
URLConnection connection = url.openConnection();
connection.addRequestProperty("Accept", "application/json");
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
ObjectMapper mapper = new ObjectMapper();
System.out.println(line);
}
if (delay > 0) {
System.out.println("Delayed.");
sleep(delay);
}
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
}
private static Thread[] generateAndStart(int num, CloseableHttpClient httpClient, HttpGet getReq, long delay) {
Thread[] workers = new Thread[num];
for (int i = 0; i < num; i++) {
System.out.println("Starting worker: " + i);
final int j = i;
workers[i] = new Thread(() -> connect(httpClient, getReq, delay, j));
workers[i].start();
}
return workers;
}
private static void connect(CloseableHttpClient httpClient, HttpGet request, long delay, int ndx) {
System.out.println(request.getURI().toString() + " started.");
try(
CloseableHttpResponse response = httpClient.execute(request, HttpClientContext.create());
BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()))) {
String line;
while ((line = reader.readLine()) != null) {
ObjectMapper mapper = new ObjectMapper();
System.out.println(line);
}
if (delay > 0) {
System.out.println("Delayed.");
sleep(delay);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void sleep(long delay) {
try {
Thread.sleep(delay);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Update 1 (28 March 2017)
I have made several observations and conclusions,
- JDK
java.net.URLConnectiondoes not make a connection untilURLConnection.getInputStream()is called. java.net.URLConnectioncloses the current socket if bad connection happens e.g. HTTP error, and creates a new socket.- Using the
java.net.URLConnectioninstances created from the samejava.net.URLinstance in a multi threaded environment will create multiple sockets to the server. Instead, for simplicity, invokeURL.openConnection()in asynchronizedblock.On the same note, it does not mean every call to theI was wrong. The number of sockets created is according to the number of threads that invokeURL.openConnection()will create a new socket. I believeURLregulates this.URL.openConnection(). - It is mentioned in many places and I mention again, closing / disconnecting the URLConnection does not close the socket.
- Connecting to a different path of the same server does not create another connection socket. In other words, persistent connection is usable with different paths.
- Apache HTTPClient in general is easier to use and more intuitive. It supports persistent connection (uses the same socket for connection) in a multi threaded environment without user's intervention.
- I could not get
URLto comply tohttp.maxConnectionsandhttp.keepAlive. For example, I include-Dhttp.keepAlive=falseduring runtime does not preventConnection: keep-aliveincluded in the HTTP headers.
My observations come from the examples I pasted here. They are better examples than the code pasted above.
java.net.HttpURLConnectionalso does connection pooling? There may be no point to this. - user207421connect()is called automatically. You don't need to call it at all. - user207421