patternjavaMinor
Efficiently using Apache HttpClient in multithreaded environment
Viewed 0 times
efficientlyenvironmenthttpclientusingapachemultithreaded
Problem
I have a library which is being used by customers and they are passing
Here is my
```
public class DataClient implements Client {
private final ExecutorService service = Executors.newFixedThreadPool(10);
private CloseableHttpClient httpClientBuilder;
// this constructor will be called only once through my factory
// so initializing defaults here
public DataClient() {
// do I need this request config object here if I am creating it in my call method as well?
RequestConfig requestConfig =
RequestConfig.custom().setConnectionRequestTimeout(500).setConnectTimeout(500)
.setSocketTimeout(500).setStaleConnectionCheckEnabled(false).build();
SocketConfig socketConfig =
SocketConfig.custom().setSoKeepAlive(true).setTcpNoDelay(true).build();
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager =
new PoolingHttpClientConnectionManager();
poolingHttpClientConnectionManager.setMaxTotal(300);
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(200);
httpClientBuilder =
HttpClientBuilder.create().setConnectionManager(poolingHttpClientConnectionManager)
.setDefaultRequestConfig(requestConfig).setDefaultSocketConfig(socketConfig).build();
}
@Override
public DataResponse getSyncData(DataRequest key) {
DataResponse response = null;
Future responseFuture = null;
try {
responseFuture = getAsyncData(key);
response = responseFuture.get(key.getTimeout(), key.getTimeoutUnit());
DataRequest object which has userid, various timeouts and some other fields in it. Now I use this DataRequest object to create a URL, then make an HTTP call using Apache HttpClient for which my service returns a JSON response which I use to create and return a DataResponse object back to them.Here is my
DataClient class used by customers by passing a DataRequest object to it:```
public class DataClient implements Client {
private final ExecutorService service = Executors.newFixedThreadPool(10);
private CloseableHttpClient httpClientBuilder;
// this constructor will be called only once through my factory
// so initializing defaults here
public DataClient() {
// do I need this request config object here if I am creating it in my call method as well?
RequestConfig requestConfig =
RequestConfig.custom().setConnectionRequestTimeout(500).setConnectTimeout(500)
.setSocketTimeout(500).setStaleConnectionCheckEnabled(false).build();
SocketConfig socketConfig =
SocketConfig.custom().setSoKeepAlive(true).setTcpNoDelay(true).build();
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager =
new PoolingHttpClientConnectionManager();
poolingHttpClientConnectionManager.setMaxTotal(300);
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(200);
httpClientBuilder =
HttpClientBuilder.create().setConnectionManager(poolingHttpClientConnectionManager)
.setDefaultRequestConfig(requestConfig).setDefaultSocketConfig(socketConfig).build();
}
@Override
public DataResponse getSyncData(DataRequest key) {
DataResponse response = null;
Future responseFuture = null;
try {
responseFuture = getAsyncData(key);
response = responseFuture.get(key.getTimeout(), key.getTimeoutUnit());
Solution
Based on my experience with a proof of concept, usage of response handlers can improve performance up to 10 times. It also avoids the necesity of explicit release of resources like the closable response.
This block of code:
can be replaced with something similar to:
This block of code:
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
HttpEntity entity = response.getEntity();
String responseBody = IOUtils.toString(entity.getContent(), StandardCharsets.UTF_8);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.OK.value()) {
// create successful DataResponse and return it
}
// otherwise log error and return DataResponse with error in it
} catch(....) {can be replaced with something similar to:
// Create a custom response handler
ResponseHandler responseHandler = new ResponseHandler() {
@Override
public String handleResponse(final HttpResponse response) throws ClientProtocolException, IOException {
int status = response.getStatusLine().getStatusCode();
if (status >= 200 && status < 300) {
HttpEntity entity = response.getEntity();
return entity != null ? EntityUtils.toString(entity) : null;
} else {
throw new ClientProtocolException("Unexpected response status: " + status);
}
}
};
String responseBody = httpclient.execute(httpget, responseHandler);
// TODO handle ClientProtocolException, raised when status code is not OKCode Snippets
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
HttpEntity entity = response.getEntity();
String responseBody = IOUtils.toString(entity.getContent(), StandardCharsets.UTF_8);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.OK.value()) {
// create successful DataResponse and return it
}
// otherwise log error and return DataResponse with error in it
} catch(....) {// Create a custom response handler
ResponseHandler<String> responseHandler = new ResponseHandler<String>() {
@Override
public String handleResponse(final HttpResponse response) throws ClientProtocolException, IOException {
int status = response.getStatusLine().getStatusCode();
if (status >= 200 && status < 300) {
HttpEntity entity = response.getEntity();
return entity != null ? EntityUtils.toString(entity) : null;
} else {
throw new ClientProtocolException("Unexpected response status: " + status);
}
}
};
String responseBody = httpclient.execute(httpget, responseHandler);
// TODO handle ClientProtocolException, raised when status code is not OKContext
StackExchange Code Review Q#128367, answer score: 4
Revisions (0)
No revisions yet.