HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Efficiently using Apache HttpClient in multithreaded environment

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
efficientlyenvironmenthttpclientusingapachemultithreaded

Problem

I have a library which is being used by customers and they are passing 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:

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 OK

Code 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 OK

Context

StackExchange Code Review Q#128367, answer score: 4

Revisions (0)

No revisions yet.