Лучшая практика использования HttpClient в многопоточной среде

85

Некоторое время я использую 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 ++?

Чеок Ян Ченг
источник

Ответы:

47

Определенно метод A, потому что он объединен и безопасен для потоков.

Если вы используете 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);

источник
49
ThreadSafeClientConnManager устарел в пользу PoolingClientConnManager в версии 4.2
Дрю Стивенс
Привет, можно ли использовать httpclient, созданный этим методом, для поддержания сеанса, как описано здесь stackoverflow.com/questions/5960832/… ...? Потому что, когда я пытался, мне не удавалось поддерживать сеанс между разными запросами ...
Сактиг
17
4.3.1 здесь: PoolingClientConnManager устарел в пользу PoolingHttpClientConnectionManager.
Матиас
@DrewStephens снова PoolingClientConnManager устарел в пользу PoolingHttpClientConnectionManager
didxga
18

Метод A рекомендуется сообществом разработчиков httpclient.

Пожалуйста, обратитесь к http://www.mail-archive.com/httpclient-users@hc.apache.org/msg02455.html для получения дополнительных сведений.

Чеок Ян Ченг
источник
1
Когда будет один вызов "выключения" диспетчера соединений, если клиент сделан глобальным.
Wand Maker
1
Какие инструменты / команды Linux полезны для отладки или «визуализации» поведения ConnectionManager под капотом? Я спрашиваю, потому что у нас сейчас проблемы с подключениями в CLOSE_WAIT и других эффектах, и мы изо всех сил пытаемся найти хороший способ увидеть, что именно происходит.
Кристоф
@WandMaker, я почти уверен, что вы просто вызовете выключение при выходе из программы или когда вы закончите какой-то пакет работы, когда вам не понадобятся какие-либо соединения в течение некоторого времени.
Николас ДиПиацца
1
@Christoph netstatотлично справляется с этим. technet.microsoft.com/en-us/sysinternals/bb897437.aspx тоже
Николас ДиПиацца, 07
13

Насколько я понимаю, 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. Этот подход предполагает, что каждый поток не остается в соединении надолго. Обратите внимание, что сам мошенник остается открытым.

джна
источник
Не получил фактического ответа на мой вопрос, какой метод (A или B) лучше.
Cheok Yan Cheng
5

Я думаю, вы захотите использовать ThreadSafeClientConnManager.

Вы можете увидеть, как это работает, здесь: http://foo.jasonhudgins.com/2009/08/http-connection-reuse-in-android.html

Или в том, AndroidHttpClientкто его использует внутри.

Томас Але
источник
1
Оппс. Нет планов по переходу с HttpClient 3.x на 4.x, так как 3.x работал безупречно в моем приложении почти 2 года :)
Cheok Yan Cheng
9
Конечно, просто если бы кто-то еще пришел сюда погуглить за ответом :)
Thomas Ahle
5

С HttpClient 4.5 вы можете сделать это:

CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(new PoolingHttpClientConnectionManager()).build();

Обратите внимание, что этот реализует Closeable (для закрытия диспетчера соединений).

Димитар II
источник