Last active
March 29, 2017 06:26
-
-
Save sshark/934fecd48618272e7754b419424ea2ad to your computer and use it in GitHub Desktop.
HTTP pool test between JDK URLConnection and Apache HTTPClient
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.teckhooi; | |
import org.apache.http.HttpHost; | |
import org.apache.http.client.methods.HttpGet; | |
import org.apache.http.client.methods.HttpPost; | |
import org.apache.http.client.protocol.HttpClientContext; | |
import org.apache.http.client.utils.URIBuilder; | |
import org.apache.http.entity.StringEntity; | |
import org.apache.http.impl.client.CloseableHttpClient; | |
import org.apache.http.impl.client.HttpClientBuilder; | |
import org.apache.http.impl.client.HttpClients; | |
import java.io.*; | |
import java.net.*; | |
import java.util.function.Supplier; | |
import static org.teckhooi.HttpPoolTest.RunOption.RunA; | |
import static org.teckhooi.HttpPoolTest.RunOption.RunB; | |
import static org.teckhooi.HttpPoolTest.RunOption.RunB1; | |
/** | |
* Bulk of the code is the same except this code does not POST to the server. It demonstrates GET from | |
* the server of different paths uses the same connection socket. | |
*/ | |
public class HttpPoolGetTest { | |
// Refactor RunOption to use direct methods | |
public enum RunOption {RunA, RunB, RunB1, RunAll} | |
public static void main(String[] args) throws Exception { | |
System.out.println("http.maxConnections (default: 5): " + System.getProperty("http.maxConnections")); | |
System.out.println("http.keepAlive (default: true): " + System.getProperty("http.keepAlive")); | |
new HttpPoolGetTest().run(new URL("http://myrest.getsandbox.com"), RunA, 100, new ProxyAddress("localhost", 8080)); | |
} | |
public void run(URL url, RunOption option, int numOfConnections, ProxyAddress proxyAddress) throws Exception { | |
switch (option) { | |
case RunA: | |
reportElaspedTime(() -> runWithHttpClient(url, numOfConnections, new HttpHost(proxyAddress.getHost(), proxyAddress.getPort()))); | |
break; | |
case RunB: | |
reportElaspedTime(() -> runWithURL(url, numOfConnections, new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyAddress.getHost(), proxyAddress.getPort())), true)); | |
break; | |
case RunB1: | |
reportElaspedTime(() -> runWithURL(url, numOfConnections, new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyAddress.getHost(), proxyAddress.getPort())), false)); | |
break; | |
default: | |
reportElaspedTime(() -> runWithHttpClient(url, numOfConnections, new HttpHost(proxyAddress.getHost(), proxyAddress.getPort()))); | |
reportElaspedTime(() -> runWithURL(url, numOfConnections, new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyAddress.getHost(), proxyAddress.getPort())), true)); | |
break; | |
} | |
} | |
private boolean runWithHttpClient(URL url, int numOfClients, HttpHost proxy) { | |
HttpClientBuilder httpClientBuilder = HttpClients.custom() | |
.setConnectionManager(cm) | |
.setMaxConnTotal(5) | |
.setMaxConnPerRoute(1); | |
CloseableHttpClient httpClient = proxy == null ? | |
httpClientBuilder.build() : | |
httpClientBuilder.setProxy(proxy).build(); | |
/* | |
Option A: Using Apache HTTP Client | |
Preferred way to do HTTP connection in term of ease of use and predictability. | |
NOTE: Connecting to a different path of the same server does not create another connection socket. | |
*/ | |
try { | |
generateAndStart(numOfClients, httpClient, new URIBuilder(url.toURI()), 0); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
return false; | |
} | |
return true; | |
} | |
private boolean runWithURL(URL url, int numClients, Proxy proxy, boolean runConcurrent) { | |
/* | |
Option B: JDK 8 URL connections | |
Bad connection will result closing current socket and use a new socket. Using URLConnection instances created | |
from the same URL instance in a multi threaded environment will create multiple sockets to the server. To use | |
persistent connection, close the input stream and request for a new URLConnection instance from the URL | |
instance again. Closing the URLConnection does not equal to closing the socket. | |
No connection is open to the server until URLConnection.getInputStream() is executed. | |
NOTE: Connecting to a different path of the same server does not create another connection socket. | |
*/ | |
try { | |
if (runConcurrent) { | |
concurrentGenerateAndStart(numClients, url, proxy, 0); | |
} else { | |
generateAndStart(numClients, url, proxy, 0); | |
} | |
} catch (Exception e) { | |
e.printStackTrace(); | |
return false; | |
} | |
return true; | |
} | |
private void concurrentGenerateAndStart(int num, URL baseUrl, Proxy proxy, long delay) throws Exception { | |
Thread[] workers = new Thread[num]; | |
for (int i = 0; i < num; i++) { | |
URL url = baseUrl.toURI().resolve("/users/" + i).toURL(); | |
URLConnection urlConnection = proxy == null ? url.openConnection() : url.openConnection(proxy); | |
urlConnection.setRequestProperty("Accept", "application/json"); | |
System.out.println("Starting JDK URL worker: " + i); | |
int ndx = i; | |
workers[i] = new Thread(() -> connect(urlConnection, delay, ndx)); | |
workers[i].start(); | |
} | |
for (int j = 0; j < workers.length; j++) { | |
try { | |
workers[j].join(); | |
} catch (InterruptedException e) { | |
e.printStackTrace(); | |
} | |
} | |
} | |
private void generateAndStart(int num, URL baseUrl, Proxy proxy, long delay) throws Exception { | |
for (int i = 0; i < num; i++) { | |
URL url = baseUrl.toURI().resolve("/users/" + i).toURL(); | |
URLConnection urlConnection = proxy == null ? url.openConnection() : url.openConnection(proxy); | |
urlConnection.setRequestProperty("Accept", "application/json"); | |
System.out.println("Starting JDK URL worker: " + i); | |
int ndx = i; | |
connect(urlConnection, delay, ndx); | |
} | |
} | |
private void connect(URLConnection urlConnection, long delay, int ndx) { | |
try { | |
System.out.println(urlConnection.getURL().toString() + " started."); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
BufferedReader reader = null; | |
try { | |
reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); | |
printResponse(reader, urlConnection.getURL().toURI().getPath(), ndx); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} finally { | |
if (reader != null) { | |
try { | |
reader.close(); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
} | |
} | |
} | |
private void generateAndStart(int num, CloseableHttpClient httpClient, URIBuilder uriBuilder, long delay) throws Exception { | |
Thread[] workers = new Thread[num]; | |
for (int i = 0; i < num; i++) { | |
System.out.println("Starting worker: " + i); | |
final int j = i; | |
uriBuilder.setPath("/users/" + i); | |
HttpGet request = new HttpGet(uriBuilder.build()); | |
request.addHeader("Content-type", "application/json"); | |
request.addHeader("Accept", "application/json"); | |
workers[i] = new Thread(() -> { | |
try { | |
connect(httpClient, request, delay, j); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
}); | |
workers[i].start(); | |
} | |
for (int i = 0; i < workers.length; i++) { | |
try { | |
workers[i].join(); | |
} catch (InterruptedException e) { | |
e.printStackTrace(); | |
} | |
} | |
} | |
private void connect(CloseableHttpClient httpClient, HttpGet request, long delay, int ndx) throws IOException { | |
System.out.println(request.getURI().toString() + " started."); | |
try ( | |
BufferedReader reader = new BufferedReader( | |
new InputStreamReader(httpClient.execute(request, HttpClientContext.create()).getEntity().getContent()))) { | |
printResponse(reader, request.getURI().getPath(), ndx); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
private void printResponse(BufferedReader reader, String message, int ndx) throws IOException { | |
String line; | |
System.out.println("Status return for " + ndx + ": " + message); | |
while ((line = reader.readLine()) != null) { | |
System.out.println(line); | |
} | |
} | |
private void sleep(long delay) { | |
try { | |
Thread.sleep(delay); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
private void reportElaspedTime(Supplier<Boolean> supplier) { | |
long start = System.currentTimeMillis(); | |
System.out.println("Time elapsed: " + (supplier.get() ? (System.currentTimeMillis() - start) : "** Exception **")); | |
} | |
static class ProxyAddress { | |
private String host; | |
private int port; | |
public ProxyAddress(String host, int port) { | |
this.host = host; | |
this.port = port; | |
} | |
public String getHost() { | |
return host; | |
} | |
public int getPort() { | |
return port; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.teckhooi; | |
import org.apache.http.HttpHost; | |
import org.apache.http.client.methods.HttpPost; | |
import org.apache.http.client.protocol.HttpClientContext; | |
import org.apache.http.entity.StringEntity; | |
import org.apache.http.impl.client.CloseableHttpClient; | |
import org.apache.http.impl.client.HttpClientBuilder; | |
import org.apache.http.impl.client.HttpClients; | |
import java.io.*; | |
import java.net.*; | |
import java.util.function.Supplier; | |
import static org.teckhooi.HttpPoolTest.RunOption.RunA; | |
import static org.teckhooi.HttpPoolTest.RunOption.RunB; | |
import static org.teckhooi.HttpPoolTest.RunOption.RunB1; | |
public class HttpPoolTest { | |
// Refactor RunOption to use direct methods | |
public enum RunOption {RunA, RunB, RunB1, RunAll} | |
public static void main(String[] args) throws Exception { | |
System.out.println("http.maxConnections (default: 5): " + System.getProperty("http.maxConnections")); | |
System.out.println("http.keepAlive (default: true): " + System.getProperty("http.keepAlive")); | |
new HttpPoolTest().run(new URL("http://myrest.getsandbox.com/users"), RunB1, 20, new ProxyAddress("localhost", 8080)); | |
} | |
public void run(URL url, RunOption option, int numOfConnections, ProxyAddress proxyAddress) throws Exception { | |
switch (option) { | |
case RunA: | |
reportElaspedTime(() -> runWithHttpClient(url, numOfConnections, new HttpHost(proxyAddress.getHost(), proxyAddress.getPort()))); | |
break; | |
case RunB: | |
reportElaspedTime(() -> runWithURL(url, numOfConnections, new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyAddress.getHost(), proxyAddress.getPort())), true)); | |
break; | |
case RunB1: | |
reportElaspedTime(() -> runWithURL(url, numOfConnections, new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyAddress.getHost(), proxyAddress.getPort())), false)); | |
break; | |
default: | |
reportElaspedTime(() -> runWithHttpClient(url, numOfConnections, new HttpHost(proxyAddress.getHost(), proxyAddress.getPort()))); | |
reportElaspedTime(() -> runWithURL(url, numOfConnections, new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyAddress.getHost(), proxyAddress.getPort())), true)); | |
break; | |
} | |
} | |
private boolean runWithHttpClient(URL url, int numOfClients, HttpHost proxy) { | |
HttpClientBuilder httpClientBuilder = HttpClients.custom() | |
.setConnectionManager(cm) | |
.setMaxConnTotal(5) | |
.setMaxConnPerRoute(1); | |
CloseableHttpClient httpClient = proxy == null ? | |
httpClientBuilder.build() : | |
httpClientBuilder.setProxy(proxy).build(); | |
/* | |
Option A: Using Apache HTTP Client | |
Preferred way to do HTTP connection in term of ease of use and predictability. | |
NOTE: Connecting to a different path of the same server does not create another connection socket. | |
*/ | |
try { | |
HttpPost request = new HttpPost(url.toURI()); | |
request.addHeader("Content-type", "application/json"); | |
request.addHeader("Accept", "application/json"); | |
generateAndStart(numOfClients, httpClient, request, 0); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
return false; | |
} | |
return true; | |
} | |
private boolean runWithURL(URL url, int numClients, Proxy proxy, boolean runConcurrent) { | |
/* | |
Option B: JDK 8 URL connections | |
Bad connection will result closing current socket and use a new socket. Using URLConnection instances created | |
from the same URL instance in a multi threaded environment will create multiple sockets to the server. To use | |
persistent connection, close the input stream and request for a new URLConnection instance from the URL | |
instance again. Closing the URLConnection does not equal to closing the socket. | |
NOTE: Connecting to a different path of the same server does not create another connection socket. | |
*/ | |
try { | |
if (runConcurrent) { | |
concurrentGenerateAndStart(numClients, url, proxy, 0); | |
} else { | |
generateAndStart(numClients, url, proxy, 0); | |
} | |
} catch (Exception e) { | |
e.printStackTrace(); | |
return false; | |
} | |
return true; | |
} | |
private void concurrentGenerateAndStart(int num, URL url, Proxy proxy, long delay) throws IOException { | |
Thread[] workers = new Thread[num]; | |
for (int i = 0; i < num; i++) { | |
HttpURLConnection urlConnection = (HttpURLConnection) (proxy == null ? url.openConnection() : url.openConnection(proxy)); | |
urlConnection.setRequestProperty("Accept", "application/json"); | |
urlConnection.setRequestProperty("Content-type", "application/json"); | |
urlConnection.setDoOutput(true); | |
System.out.println("Starting JDK URL worker: " + i); | |
int ndx = i; | |
workers[i] = new Thread(() -> connect(urlConnection, delay, ndx)); | |
workers[i].start(); | |
} | |
for (int j = 0; j < workers.length; j++) { | |
try { | |
workers[j].join(); | |
} catch (InterruptedException e) { | |
e.printStackTrace(); | |
} | |
} | |
} | |
private void generateAndStart(int num, URL url, Proxy proxy, long delay) throws IOException { | |
for (int i = 0; i < num; i++) { | |
HttpURLConnection urlConnection = (HttpURLConnection) (proxy == null ? url.openConnection() : url.openConnection(proxy)); | |
urlConnection.setRequestProperty("Accept", "application/json"); | |
urlConnection.setRequestProperty("Content-type", "application/json"); | |
urlConnection.setDoOutput(true); | |
System.out.println("Starting JDK URL worker: " + i); | |
int ndx = i; | |
connect(urlConnection, delay, ndx); | |
} | |
} | |
private void connect(URLConnection urlConnection, long delay, int ndx) { | |
try { | |
System.out.println(urlConnection.getURL().toString() + " started."); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
String message = "{\"name\":\"Mr. " + i + "\"}"; | |
PrintWriter writer = null; | |
BufferedReader reader = null; | |
try { | |
writer = new PrintWriter(new OutputStreamWriter(urlConnection.getOutputStream())); | |
writer.print(message); | |
writer.flush(); | |
reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); | |
printResponse(reader, message, ndx); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} finally { | |
if (writer != null) { | |
writer.close(); | |
} | |
if (reader != null) { | |
try { | |
reader.close(); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
} | |
} | |
} | |
private void generateAndStart(int num, CloseableHttpClient httpClient, HttpPost request, long delay) throws Exception { | |
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(() -> { | |
try { | |
connect(httpClient, request, delay, j); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
}); | |
workers[i].start(); | |
} | |
for (int i = 0; i < workers.length; i++) { | |
try { | |
workers[i].join(); | |
} catch (InterruptedException e) { | |
e.printStackTrace(); | |
} | |
} | |
} | |
private void connect(CloseableHttpClient httpClient, HttpPost request, long delay, int ndx) throws IOException { | |
System.out.println(request.getURI().toString() + " started."); | |
String message = "{\"name\":\"Mr. " + i + "\"}"; | |
request.setEntity(new StringEntity(message)); | |
try ( | |
BufferedReader reader = new BufferedReader( | |
new InputStreamReader(httpClient.execute(request, HttpClientContext.create()).getEntity().getContent()))) { | |
printResponse(reader, message, ndx); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
private void printResponse(BufferedReader reader, String message, int ndx) throws IOException { | |
String line; | |
System.out.println("Status return for " + ndx + ": " + message); | |
while ((line = reader.readLine()) != null) { | |
System.out.println(line); | |
} | |
} | |
private void sleep(long delay) { | |
try { | |
Thread.sleep(delay); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
private void reportElaspedTime(Supplier<Boolean> supplier) { | |
long start = System.currentTimeMillis(); | |
System.out.println("Time elapsed: " + (supplier.get() ? (System.currentTimeMillis() - start) : "** Exception **")); | |
} | |
static class ProxyAddress { | |
private String host; | |
private int port; | |
public ProxyAddress(String host, int port) { | |
this.host = host; | |
this.port = port; | |
} | |
public String getHost() { | |
return host; | |
} | |
public int getPort() { | |
return port; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment