Лучшая практика использования HttpClient в многопоточной среде
-
16-09-2019 - |
Вопрос
Некоторое время я использую HttpClient в многопоточной среде.Для каждого потока, когда он инициирует соединение, он создает совершенно новый экземпляр HttpClient.
Недавно я обнаружил, что использование этого подхода может привести к тому, что у пользователя будет открыто слишком много портов, и большинство соединений будут находиться в состоянии TIME_WAIT.
http://www.opensubscriber.com/message/commons-httpclient-dev@jakarta.apache.org/86045.html
Следовательно, вместо того, чтобы каждый поток выполнял:
HttpClient c = new HttpClient();
try {
c.executeMethod(method);
}
catch(...) {
}
finally {
method.releaseConnection();
}
Мы планируем иметь:
[МЕТОД А]
// global_c is initialized once through
// HttpClient global_c = new HttpClient(new MultiThreadedHttpConnectionManager());
try {
global_c.executeMethod(method);
}
catch(...) {
}
finally {
method.releaseConnection();
}
В обычной ситуации к global_c будут обращаться одновременно 50++ потоков.Мне было интересно, создаст ли это какие-либо проблемы с производительностью?Использует ли MultiThreadedHttpConnectionManager механизм без блокировки для реализации своей политики потокобезопасности?
Если 10 потоков используют global_c, будут ли заблокированы остальные 40 потоков?
Или было бы лучше, если бы в каждом потоке я создавал экземпляр HttpClient, но явно освобождал диспетчер соединений?
[МЕТОД Б]
MultiThreadedHttpConnectionManager connman = new MultiThreadedHttpConnectionManager();
HttpClient c = new HttpClient(connman);
try {
c.executeMethod(method);
}
catch(...) {
}
finally {
method.releaseConnection();
connman.shutdown();
}
Будет ли у connman.shutdown() проблемы с производительностью?
Могу ли я узнать, какой метод (A или B) лучше для приложения, использующего потоки 50++?
Решение 2
Метод А рекомендован сообществом разработчиков httpclient.
Пожалуйста, обратитесь http://www.mail-archive.com/httpclient-users@hc.apache.org/msg02455.html Больше подробностей.
Другие советы
Определенно метод А, потому что он объединен в пул и потокобезопасен.
Если вы используете httpclient 4.x, диспетчер соединений вызывается ThreadSafeClientConnManager.Видеть это связь для получения более подробной информации (прокрутите вниз до «Диспетчер соединений пула»).Например:
HttpParams params = new BasicHttpParams();
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
ClientConnectionManager cm = new ThreadSafeClientConnManager(params, registry);
HttpClient client = new DefaultHttpClient(cm, params);
Насколько я понимаю в документации, HttpConnection сам по себе не считается потокобезопасным, и, следовательно, MultiThreadedHttpConnectionManager предоставляет многоразовый пул HttpConnections, у вас есть один MultiThreadedHttpConnectionManager, общий для всех потоков и инициализируемый ровно один раз.Поэтому к варианту А нужно внести пару небольших уточнений.
MultiThreadedHttpConnectionManager connman = new MultiThreadedHttpConnectionManag
Тогда каждый поток должен использовать последовательность для каждого запроса, получая соединение из пула и возвращая его обратно после завершения своей работы - может быть полезно использовать блокfinally.Вам также следует закодировать возможность того, что в пуле нет доступных соединений, и обработать исключение тайм-аута.
HttpConnection connection = null
try {
connection = connman.getConnectionWithTimeout(
HostConfiguration hostConfiguration, long timeout)
// work
} catch (/*etc*/) {/*etc*/} finally{
if ( connection != null )
connman.releaseConnection(connection);
}
Поскольку вы используете пул соединений, вы фактически не будете закрывать соединения, поэтому это не должно привести к проблеме TIME_WAIT.Этот подход предполагает, что каждый поток не удерживает соединение надолго.Обратите внимание, что сам conman остается открытым.
Я думаю, вам захочется использовать ThreadSafeClientConnManager.
Вы можете увидеть, как это работает здесь: http://foo.jasonhudgins.com/2009/08/http-connection-reuse-in-android.html
Или в AndroidHttpClient
который использует его внутри себя.
С HttpClient 4.5 вы можете сделать это:
CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(new PoolingHttpClientConnectionManager()).build();
Обратите внимание, что здесь реализован Closeable (для закрытия диспетчера соединений).