Найкраща практика використання HttpClient у багатопотоковому середовищі


84

Деякий час я використовую 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();
}

У звичайній ситуації глобальний_с буде доступний одночасно за допомогою 50 ++ потоків. Мені цікаво, чи це створить якісь проблеми з продуктивністю? Чи використовує MultiThreadedHttpConnectionManager механізм без блокування для реалізації своєї політики безпечного потоку?

Якщо 10 потоків використовують global_c, чи заблокуються інші 40 потоків?

Або було б краще, якщо б у кожному потоці я створював екземпляр HttpClient, але явно випускав менеджер з'єднань?

[МЕТОД B]

MultiThreadedHttpConnectionManager connman = new MultiThreadedHttpConnectionManager();
HttpClient c = new HttpClient(connman);
try {
      c.executeMethod(method);
}
catch(...) {
}
finally {
    method.releaseConnection();
    connman.shutdown();
}

Чи буде connman.shutdown () страждати від проблем із продуктивністю?

Чи можу я знати, який метод (A або B) є кращим для застосування з використанням потоків 50 ++?

Відповіді:


46

Безумовно, метод 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/… ...? Тому що, коли я намагався, я не зміг підтримувати сесію для різних запитів ...
sakthig

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 теж
Ніколас ДіП'яцца

13

Я прочитав документи, що HttpConnection сам по собі не розглядається як безпечний для потоків, а отже MultiThreadedHttpConnectionManager забезпечує багаторазовий пул HttpConnections, у вас є один MultiThreadedHttpConnectionManager, спільний для всіх потоків і ініціалізований рівно один раз. Тож вам потрібно кілька невеликих уточнень до варіанту А.

MultiThreadedHttpConnectionManager connman = new MultiThreadedHttpConnectionManag

Тоді кожен потік повинен використовувати послідовність для кожного запиту, отримуючи зв'язок з пулу і повертаючи його після завершення своєї роботи - використання блоку нарешті може бути добре. Вам також слід кодувати можливість того, що у пулу відсутні доступні з'єднання, і обробити виняток часу очікування.

HttpConnection connection = null
try {
    connection = connman.getConnectionWithTimeout(
                        HostConfiguration hostConfiguration, long timeout) 
    // work
} catch (/*etc*/) {/*etc*/} finally{
    if ( connection != null )
        connman.releaseConnection(connection);
}

Оскільки ви використовуєте пул з'єднань, ви фактично не закриватимете з'єднання, і тому це не повинно спричинити проблему TIME_WAIT. Цей підхід передбачає, що кожен потік не тримається на з'єднанні довго. Зверніть увагу, що сам конман залишається відкритим.


Не отримав фактичної відповіді на моє запитання, який метод (А чи Б) кращий.
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

4

За допомогою HttpClient 4.5 ви можете зробити це:

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

Зверніть увагу, що в цьому застосовано Closeable (для вимкнення диспетчера з'єднань).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.