Как использовать HttpAsyncClient с многопоточными операциями?
Тесно связан с этим вопросом: как использовать HttpClient с многопоточными операциями? Мне интересно, является ли Apache HttpAsyncClient потокобезопасным, или он также требует использования MultiThreadedHttpConnectionManager или ThreadSafeClientConnManager.
Если для этого требуется такой диспетчер соединений, существует ли он в асинхронных библиотеках?
Мне удалось найти PoolingClientAsyncConnectionManager в асинхронных библиотеках, но я не уверен, что это то, что мне нужно.
С другой стороны, я думал об использовании ThreadLocal для создания одного объекта HttpAsyncClient на поток.
Обратите внимание, что в отличие от вопроса, на который я ссылался ранее, мне нужно, чтобы состояние было независимым между сеансами, даже если несколько сеансов затрагивают один и тот же домен. Если в сеансе 1 установлен файл cookie, он не должен быть виден в сеансе 2. По этой причине я также рассмотрел возможность создания нового объекта HttpAsyncClient для каждого отдельного запроса, хотя у меня сложилось впечатление, что должен быть лучший способ,
Благодарю.
2 ответа
После нагрузочного тестирования как с PoolingClientAsyncConnectionManager, так и без него мы обнаружили, что мы получили противоречивые результаты, когда не использовали PoolingClientAsyncConnectionManager.
Среди прочего, мы отслеживали количество Http-вызовов, которые мы делали, и количество Http-вызовов, которые были завершены (с помощью отмененных (...), завершенных (...) или сбойных (...) функций связанного FutureCallback). Без PoolingClientAsyncConnectionManager и при большой нагрузке эти две цифры иногда не совпадали, что наводило нас на мысль, что где-то некоторые соединения растоптаны информацией о соединении из других потоков (только предположение).
В любом случае, с помощью PoolingClientAsyncConnectionManager показатели всегда совпадали, и нагрузочные тесты были успешными, поэтому мы определенно используем его.
Окончательный код, который мы использовали, выглядит так:
public class RequestProcessor {
private RequestProcessor instance = new RequestProcessor();
private PoolingClientAsyncConnectionManager pcm = null;
private HttpAsyncClient httpAsyncClient = null;
private RequestProcessor() {
// Initialize the PoolingClientAsyncConnectionManager, and the HttpAsyncClient
}
public void process(...) {
this.httpAsyncClient.execute(httpMethod,
new BasicHttpContext(), // Use a separate HttpContext for each request so information is not shared between requests
new FutureCallback<HttpResponse>() {
@Override
public void cancelled() {
// Do stuff
}
@Override
public void completed(HttpResponse httpResponse) {
// Do stuff
}
@Override
public void failed(Exception e) {
// Do stuff
}
});
}
}
Вы упоминаете "независимый через сессии". Если это просто означает, что куки, то я бы подумал создать свой собственный CookieStore
который очищается, когда каждый из ваших потоков идет использовать HttpClient
было бы достаточно.
я хотел бы использовать ThreadLocal
чтобы создать клиент для каждого потока, не используйте диспетчер общих подключений, а затем агрессивно очищайте файлы cookie. Этот ответ был полезен при очистке файлов cookie:
Что-то вроде следующего кода будет работать. Я переопределил ThreadLocal.get()
метод для вызова clear()
в случае, если каждый запрос независим. Вы также можете позвонить ясно в execute(...)
метод.
private static final ThreadLocal<ClientContext> localHttpContext =
new ThreadLocal<ClientContext> () {
@Override
protected ClientContext initialValue() {
return new ClientContext();
}
@Override
public ClientContext get() {
ClientContext clientContext = super.get();
// could do this to clear the context before usage by the thread
clientContext.clear();
return clientContext;
}
};
...
ClientContext clientContext = localHttpContext.get();
// if this wasn't in the get method above
// clientContext.clear();
HttpGet httpGet = new HttpGet("http://www.google.com/");
HttpResponse response = clientContext.execute(httpGet);
...
private static class ClientContext {
final HttpClient httpClient = new DefaultHttpClient();
final CookieStore cookieStore = new BasicCookieStore();
final HttpContext localContext = new BasicHttpContext();
public ClientContext() {
// bind cookie store to the local context
localContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);
}
public HttpResponse execute(HttpUriRequest request) {
// in case you want each execute to be indepedent
// clientContext.clear();
return httpClient.execute(request, httpContext);
}
public void clear() {
cookieStore.clear();
}
}