Melhor prática para usar HttpClient em ambiente multithread

84

Por um tempo, tenho usado HttpClient em um ambiente multithread. Para cada thread, quando ele inicia uma conexão, ele criará uma instância HttpClient completamente nova.

Recentemente, descobri que, usando essa abordagem, pode fazer com que o usuário tenha muitas portas sendo abertas, e a maioria das conexões estão no estado TIME_WAIT.

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

Portanto, em vez de cada thread fazer:

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

Pretendemos ter:

[MÉTODO A]

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

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

Em uma situação normal, global_c será acessado por 50 ++ threads simultaneamente. Eu queria saber, isso criará problemas de desempenho? O MultiThreadedHttpConnectionManager está usando um mecanismo sem bloqueio para implementar sua política de thread-safe?

Se 10 threads estiverem usando global_c, as outras 40 threads serão bloqueadas?

Ou seria melhor se, em cada thread, eu criasse uma instância de um HttpClient, mas liberasse o gerenciador de conexões explicitamente?

[MÉTODO B]

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

O connman.shutdown () sofrerá problemas de desempenho?

Posso saber qual método (A ou B) é melhor, para aplicativos que usam threads de 50 ++?

Cheok Yan Cheng
fonte

Respostas:

46

Definitivamente o Método A porque é pool e thread-safe.

Se você estiver usando httpclient 4.x, o gerenciador de conexões é denominado ThreadSafeClientConnManager . Consulte este link para obter mais detalhes (role para baixo até "Pooling connection manager"). Por exemplo:

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

fonte
49
ThreadSafeClientConnManager foi preterido em favor de PoolingClientConnManager em 4.2
Drew Stephens
Olá, o httpclient criado por este método pode ser usado para manter a sessão conforme descrito aqui stackoverflow.com/questions/5960832/… ...? Porque quando tentei, não consegui manter a sessão em diferentes solicitações ...
sakthig
17
4.3.1 aqui: PoolingClientConnManager foi preterido em favor de PoolingHttpClientConnectionManager.
Matthias
@DrewStephens Again PoolingClientConnManager foi preterido em favor de PoolingHttpClientConnectionManager
didxga
18

O método A é recomendado pela comunidade de desenvolvedores httpclient.

Consulte http://www.mail-archive.com/[email protected]/msg02455.html para obter mais detalhes.

Cheok Yan Cheng
fonte
1
Quando uma chamada "desligará" no gerenciador de conexões se o cliente se tornar global.
Wand Maker
1
Quais ferramentas / comandos do Linux são úteis para depurar ou "visualizar" o comportamento do ConnectionManager nos bastidores? Eu pergunto porque atualmente temos problemas com conexões em CLOSE_WAIT e outros efeitos e estamos lutando para encontrar uma boa maneira de ver o que exatamente está acontecendo.
Christoph
@WandMaker, tenho certeza de que você apenas chamaria o desligamento quando qualquer um dos programas fosse encerrado ou quando tiver terminado algum trabalho em que não precise de nenhuma conexão por algum tempo.
Nicholas DiPiazza
1
@Christoph netstatfaz um ótimo trabalho nisso. technet.microsoft.com/en-us/sysinternals/bb897437.aspx também
Nicholas DiPiazza
13

Minha leitura dos documentos é que HttpConnection em si não é tratado como thread-safe e, portanto, MultiThreadedHttpConnectionManager fornece um pool reutilizável de HttpConnections, você tem um único MultiThreadedHttpConnectionManager compartilhado por todos os threads e inicializado exatamente uma vez. Portanto, você precisa de alguns pequenos refinamentos para a opção A.

MultiThreadedHttpConnectionManager connman = new MultiThreadedHttpConnectionManag

Então, cada thread deve usar a sequência para cada solicitação, obter uma conexão do pool e colocá-la de volta na conclusão de seu trabalho - usar um bloco finally pode ser bom. Você também deve codificar a possibilidade de que o pool não tenha conexões disponíveis e processar a exceção de tempo limite.

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

Como você está usando um pool de conexões, não estará realmente fechando as conexões e, portanto, não deve ocorrer o problema TIME_WAIT. Essa abordagem pressupõe que cada thread não se agarra à conexão por muito tempo. Observe que o próprio conman fica aberto.

djna
fonte
Não respondi à minha pergunta sobre qual método (A ou B) é melhor.
Cheok Yan Cheng
5

Acho que você vai querer usar ThreadSafeClientConnManager.

Você pode ver como funciona aqui: http://foo.jasonhudgins.com/2009/08/http-connection-reuse-in-android.html

Ou no AndroidHttpClientqual o utiliza internamente.

Thomas Ahle
fonte
1
Ops. Não há planos de migrar do HttpClient 3.x para 4.x, já que o 3.x estava funcionando perfeitamente em meu aplicativo por quase 2 anos :)
Cheok Yan Cheng
9
Claro, apenas se alguém vier aqui procurando uma resposta no Google :)
Thomas Ahle
4

Com o HttpClient 4.5, você pode fazer isso:

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

Observe que este implementa Closeable (para desligar o gerenciador de conexões).

Dimitar II
fonte