Как эффективно использовать RestTemplate в многопоточном приложении?
Я использую RestTemplate
как мой HttpClient
в одной из моих библиотек. Я не уверен, правильно ли я использую его в многопоточной среде, поскольку моя библиотека будет использоваться под очень большой нагрузкой в многопоточной среде, поэтому она должна быть очень быстрой.
Ниже приведен мой класс DataClient:
public class DataClient implements Client {
private RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory());
private ExecutorService executor = Executors.newFixedThreadPool(10);
// for synchronous call
@Override
public DataResponse executeSync(DataKey key) {
DataResponse dataResponse = null;
Future<DataResponse> future = null;
try {
future = executeAsync(key);
dataResponse = future.get(key.getTimeout(), TimeUnit.MILLISECONDS);
} catch (TimeoutException ex) {
dataResponse = new DataResponse(null, DataErrorEnum.TIMEOUT, DataStatusEnum.ERROR);
future.cancel(true);
} catch (Exception ex) {
dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR);
}
return dataResponse;
}
//for asynchronous call
@Override
public Future<DataResponse> executeAsync(DataKey key) {
Future<DataResponse> future = null;
Task task = new Task(key, restTemplate);
future = executor.submit(task);
return future;
}
// does this looks right?
private ClientHttpRequestFactory clientHttpRequestFactory() {
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
// setting 2000 ms as the default timeout for each Http Request
RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(2000).setConnectTimeout(2000)
.setSocketTimeout(2000).setStaleConnectionCheckEnabled(false).build();
SocketConfig socketConfig = SocketConfig.custom().setSoKeepAlive(true).setTcpNoDelay(true).build();
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
poolingHttpClientConnectionManager.setMaxTotal(800);
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(700);
CloseableHttpClient httpClientBuilder = HttpClientBuilder.create()
.setConnectionManager(poolingHttpClientConnectionManager).setDefaultRequestConfig(requestConfig)
.setDefaultSocketConfig(socketConfig).build();
requestFactory.setHttpClient(httpClientBuilder);
return requestFactory;
}
}
Простой класс, который будет выполнять актуальную задачу:
public class Task implements Callable<DataResponse> {
private final DataKey key;
private final RestTemplate restTemplate;
public Task(DataKey key, RestTemplate restTemplate) {
this.key = key;
this.restTemplate = restTemplate;
}
@Override
public DataResponse call() {
DataResponse dataResponse = null;
String response = null;
try {
String url = createURL();
response = restTemplate.getForObject(url, String.class);
dataResponse = new DataResponse(response, DataErrorEnum.OK, DataStatusEnum.SUCCESS);
} catch (RestClientException ex) {
dataResponse = new DataResponse(null, DataErrorEnum.SERVER_DOWN, DataStatusEnum.ERROR);
} catch (Exception ex) {
dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR);
}
return dataResponse;
}
}
И ниже моя фабрика, которую я использую для создания единственного экземпляра DataClient, что означает, что он также будет иметь один экземпляр пример RestTemplate
.
public class DataClientFactory {
private DataClientFactory() {}
private static class ClientHolder {
private static final DataClient INSTANCE = new DataClient();
}
public static Client getInstance() {
return ClientHolder.INSTANCE;
}
}
И вот как я сделаю вызов, чтобы получить данные:
DataResponse response = DataClientFactory.getInstance().executeSync(dataKey);
Теперь мой вопрос - я не уверен, правильно ли я использую RestTemplate
с HttpComponentsClientHttpRequestFactory
. Нужен ли мне PoolingHttpClientConnectionManager
здесь вместе с RestTemplate
вообще или нет?
Моя главная цель-эффективно использовать RestTemplate
в многопоточной среде. Поскольку моя библиотека будет использоваться под очень большой нагрузкой, поэтому она должна быть очень быстрой. Как и при большой нагрузке, я видел много соединений TIME_WAIT, поэтому я добавил clientHttpRequestFactory()
метод для использования с RestTemplate
.3 ответа:
RestTemplate
являетсяпотокобезопасным весной. Поэтому вы можете создать только один экземплярRestTemplate
в своем приложении и поделиться им с несколькими потоками. Это,конечно, предполагает, что вы будете использовать одни и те же свойства HTTP(например, timeout, set alive и т. д.) Для всех. В случае, если вам нужно изменить свойства соединения, вы можете создать пул в начале приложения объектовRestTemplate
и использовать его для внедрения экземпляра RestTemplate в вызывающий класс.
Если все запросы, которые вы делаете на restTemplate, будут выполняться через исполнителя
Тем не менее, лучше, если вы используетеExecutorService executor = Executors.newFixedThreadPool(10);
, то таким образом вы сами управляете пулом соединений restTemplate. Нет необходимости в других менеджерах соединений.PoolingHttpClientConnectionManager
со всеми необходимыми настройками (таймауты, номер соединения и т. д.).В результате вы пишете гораздо меньше кода, так как вам больше не нужен исполнитель пула фиксированных потоков, потому что каждый запрос, который вы делаете на restTemplate, получит как (то, что вы сделали выше):
final Future<CPoolEntry> future = this.pool.lease(..)
Наконец, если вам нужны асинхронные вызовы, возможно, стоит попробовать http://hc.apache.org/httpcomponents-asyncclient-4.1.x/index.html .
RestTemplate
сам по себе потокобезопасен . Однако я отмечаю, что вашprivate RestTemplate restTemplate
не являетсяfinal
. Поэтому не ясно, что это безопасно опубликовано из конструктораDataClient
. Ссылка никогда не изменяется, поэтому вы можете просто изменить ее наfinal
, чтобы быть уверенным. К счастью, Ссылка будет безопасно опубликована до того, как любая из ваших задач попытается ее использовать, потому чтоExecutorService
дает такую гарантию, поэтому я считаю, что ваш код потокобезопасен.