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<DataResponse> responseFuture = null;
try {
responseFuture = getAsyncData(key);
response = responseFuture.get(key.getTimeout(), key.getTimeoutUnit());
} catch (TimeoutException ex) {
response = new DataResponse(DataErrorEnum.CLIENT_TIMEOUT, DataStatusEnum.ERROR);
responseFuture.cancel(true);
// logging exception here
}
return response;
}
@Override
public Future<DataResponse> getAsyncData(DataRequest key) {
DataFetcherTask task = new DataFetcherTask(key, httpClientBuilder);
Future<DataResponse> future = service.submit(task);
return future;
}
}
DataFetcherTask
class:
public class DataFetcherTask implements Callable<DataResponse> {
private final DataRequest key;
private final CloseableHttpClient httpClientBuilder;
public DataFetcherTask(DataRequest key, CloseableHttpClient httpClientBuilder) {
this.key = key;
this.httpClientBuilder = httpClientBuilder;
}
// In a nutshell below is what I am doing here.
// 1. Make an url using DataRequest key.
// 2. And then execute the url using Apache HttpClient.
// 3. Make a DataResponse object and return it.
@Override
public DataResponse call() throws Exception {
// some code to find out the "hostnames"
for (String hostname : hostnames) {
try {
HttpGet httpGet = new HttpGet(makeURL(hostname));
httpGet.setConfig(makeConfig());
httpGet.addHeader(key.getHeader());
// closing CloseableHttpResponse in try resource block
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 (InterruptedIOException ex) {
// usually comes here if there is socket timeout or connect timed out
// log error and call another machine
} catch (IOException ex) {
// log error
}
}
// not sure whether I can use copy method of RequestConfig here
private RequestConfig makeConfig() {
return RequestConfig.custom().setConnectionRequestTimeout(key.getConnectionRequestTimeout())
.setConnectTimeout(key.getConnectTimeout()).setSocketTimeout(key.getSocketTimeout())
.setStaleConnectionCheckEnabled(false).build();
}
}
}
A customer within our company will use my library like this using my factory in their code base:
// if they are calling `getSyncData()` method
DataResponse response = DataClientFactory.getInstance().getSyncData(key);
// and if they want to call `getAsyncData()` method
Future<DataResponse> response = DataClientFactory.getInstance().getAsyncData(key);
I am implementing sync call as async + waiting
since I want to throttle them with the number of threads otherwise they can bombard our service without any control. My library will be used by lots of customers within our company and their applications won't ever shutdown, they will keep running all the time. The only thing that will happen is their machines will get restarted, that's all. I have to use timeout values present in my DataRequest
class in my Apache HttpClient
calls so that's why I am creating a RequestConfig
and using it with HttpClient
for each request. I am on Java 7.
I'd like to know if there's any efficient way of using Apache HttpClient
in a multithreaded environment in production.