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/commons-httpclient-dev@jakarta.apache.org/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 ++?

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

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/httpclient-users@hc.apache.org/msg02455.html .


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.


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.


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ń).

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.