Najlepsze rozwiązania dotyczące używania HttpClient w środowisku wielowątkowym

84

Od jakiegoś czasu używam HttpClient w środowisku wielowątkowym. Dla każdego wątku, kiedy inicjuje połączenie, utworzy zupełnie nowe wystąpienie HttpClient.

Niedawno odkryłem, że stosując to podejście, może to spowodować, że użytkownik otworzy zbyt wiele portów, a większość połączeń jest w stanie TIME_WAIT.

http://www.opensubscriber.com/message/[email protected]/86045.html

Dlatego zamiast robić każdy wątek:

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

Planujemy mieć:

[METODA A]

// global_c is initialized once through
// HttpClient global_c = new HttpClient(new MultiThreadedHttpConnectionManager());

try {
    global_c.executeMethod(method);
}
catch(...) {
}
finally {
    method.releaseConnection();
}

W normalnej sytuacji do global_c będzie miało dostęp 50 ++ wątków jednocześnie. Zastanawiałem się, czy spowoduje to jakiekolwiek problemy z wydajnością? Czy MultiThreadedHttpConnectionManager używa mechanizmu bez blokady do implementacji zasad bezpieczeństwa wątków?

Jeśli 10 wątków używa global_c, czy pozostałe 40 wątków zostanie zablokowanych?

A może byłoby lepiej, jeśli w każdym wątku utworzę wystąpienie HttpClient, ale jawnie zwolnię menedżera połączeń?

[METODA B]

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

Czy w przypadku connman.shutdown () występują problemy z wydajnością?

Czy mogę wiedzieć, która metoda (A lub B) jest lepsza dla aplikacji korzystającej z wątków 50 ++?

Cheok Yan Cheng
źródło

Odpowiedzi:

46

Zdecydowanie metoda A, ponieważ jest w puli i bezpieczna wątkowo.

Jeśli używasz protokołu httpclient 4.x, menedżer połączeń nazywa się ThreadSafeClientConnManager . Zobacz to łącze, aby uzyskać więcej informacji (przewiń w dół do „Menedżera połączeń puli”). Na przykład:

    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);

źródło
49
ThreadSafeClientConnManager został wycofany na korzyść PoolingClientConnManager w wersji 4.2
Drew Stephens
Cześć, czy httpclient utworzony tą metodą może służyć do utrzymywania sesji w sposób opisany tutaj stackoverflow.com/questions/5960832/ ...? Ponieważ kiedy próbowałem, nie byłem w stanie utrzymać sesji dla różnych żądań ...
sakthig
17
4.3.1 tutaj: PoolingClientConnManager został wycofany na korzyść PoolingHttpClientConnectionManager.
Matthias,
@DrewStephens Again PoolingClientConnManager został deprecatd na korzyść PoolingHttpClientConnectionManager
didxga
18

Metoda A jest zalecana przez społeczność programistów httpclient.

Więcej informacji można znaleźć pod adresem http://www.mail-archive.com/[email protected]/msg02455.html .

Cheok Yan Cheng
źródło
1
Kiedy jedno wywołanie „zamknie się” w menedżerze połączeń, jeśli klient jest ustawiony jako globalny.
Wand Maker,
1
Które narzędzia / polecenia Linuksa są przydatne do debugowania lub „wizualizacji” zachowania ConnectionManager pod maską? Pytam, ponieważ obecnie mamy problem z połączeniami w CLOSE_WAIT i innymi efektami i staramy się znaleźć dobry sposób, aby zobaczyć, co dokładnie się dzieje.
Christoph
@WandMaker Jestem prawie pewien, że po prostu wywołałbyś zamknięcie systemu, gdy program zakończy działanie lub gdy skończysz pracę, w której przez jakiś czas nie będziesz potrzebować żadnych połączeń.
Nicholas DiPiazza
1
@Christoph netstatwykonuje przy tym naprawdę dobrą robotę. technet.microsoft.com/en-us/sysinternals/bb897437.aspx też
Nicholas DiPiazza
13

Czytam dokumentację, że sam HttpConnection nie jest traktowany jako bezpieczny wątkowo, a zatem MultiThreadedHttpConnectionManager zapewnia pulę HttpConnections wielokrotnego użytku, masz pojedynczy MultiThreadedHttpConnectionManager współużytkowany przez wszystkie wątki i zainicjowany dokładnie raz. Potrzebujesz więc kilku drobnych ulepszeń opcji A.

MultiThreadedHttpConnectionManager connman = new MultiThreadedHttpConnectionManag

Następnie każdy wątek powinien używać sekwencji dla każdego żądania, uzyskując połączenie z puli i umieszczając je z powrotem po zakończeniu pracy - użycie bloku last może być dobre. Należy również zakodować możliwość, że pula nie ma dostępnych połączeń i przetworzyć wyjątek limitu czasu.

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

Ponieważ używasz puli połączeń, w rzeczywistości nie będziesz zamykać połączeń, więc nie powinno to mieć problemu z TIME_WAIT. To podejście zakłada, że ​​każdy wątek nie zatrzymuje się długo na połączeniu. Zauważ, że sam conman pozostaje otwarty.

djna
źródło
Nie odpowiedziałem na moje pytanie, która metoda (A lub B) jest lepsza.
Cheok Yan Cheng
5

Myślę, że będziesz chciał użyć ThreadSafeClientConnManager.

Możesz zobaczyć, jak to działa, tutaj: http://foo.jasonhudgins.com/2009/08/http-connection-reuse-in-android.html

Lub w AndroidHttpClientktórym używa go wewnętrznie.

Thomas Ahle
źródło
1
Ups. Brak planu migracji z HttpClient 3.x do 4.x, ponieważ 3.x działał bezbłędnie w mojej aplikacji przez prawie ~ 2 lata :)
Cheok Yan Cheng
9
Jasne, gdyby ktoś inny przyszedł tutaj, szukając odpowiedzi w Google :)
Thomas Ahle
4

Za pomocą HttpClient 4.5 możesz to zrobić:

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

Zauważ, że ten implementuje Closeable (do zamykania menedżera połączeń).

Dimitar II
źródło