Práctica recomendada para usar HttpClient en un entorno multiproceso

84

Durante un tiempo, he estado usando HttpClient en un entorno multiproceso. Para cada hilo, cuando inicia una conexión, creará una instancia de HttpClient completamente nueva.

Recientemente, descubrí que, al usar este enfoque, puede hacer que el usuario tenga demasiados puertos abiertos y la mayoría de las conexiones estén en estado TIME_WAIT.

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

Por lo tanto, en lugar de hacer cada hilo:

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

Planeamos tener:

[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();
}

En una situación normal, 50 ++ subprocesos accederán simultáneamente a global_c. Me preguntaba, ¿esto creará problemas de rendimiento? ¿MultiThreadedHttpConnectionManager utiliza un mecanismo sin bloqueo para implementar su política segura para subprocesos?

Si 10 subprocesos utilizan global_c, ¿se bloquearán los otros 40 subprocesos?

¿O sería mejor si, en cada hilo, creo una instancia de un HttpClient, pero libero el administrador de conexión explícitamente?

[MÉTODO B]

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

¿Connman.shutdown () sufrirá problemas de rendimiento?

¿Puedo saber qué método (A o B) es mejor para la aplicación con hilos 50 ++?

Cheok Yan Cheng
fuente

Respuestas:

46

Definitivamente el Método A porque es agrupado y seguro para subprocesos.

Si está utilizando httpclient 4.x, el administrador de conexiones se llama ThreadSafeClientConnManager . Consulte este enlace para obtener más detalles (desplácese hacia abajo hasta "Administrador de conexiones de agrupación"). Por ejemplo:

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

fuente
49
ThreadSafeClientConnManager ha quedado obsoleto a favor de PoolingClientConnManager en 4.2
Drew Stephens
Hola, ¿se puede utilizar el httpclient creado por este método para mantener la sesión como se describe aquí stackoverflow.com/questions/5960832/… ...? Porque cuando lo intenté, no pude mantener la sesión en diferentes solicitudes ...
sakthig
17
4.3.1 aquí: PoolingClientConnManager ha quedado obsoleto en favor de PoolingHttpClientConnectionManager.
Matthias
@DrewStephens Again PoolingClientConnManager fue obsoleto a favor de PoolingHttpClientConnectionManager
didxga
18

El método A es recomendado por la comunidad de desarrolladores de httpclient.

Consulte http://www.mail-archive.com/[email protected]/msg02455.html para obtener más detalles.

Cheok Yan Cheng
fuente
1
¿Cuándo se llamará a "apagar" en el administrador de conexiones si el cliente se hace global?
Wand Maker
1
¿Qué herramientas / comandos de Linux son útiles para depurar o "visualizar" el comportamiento del ConnectionManager bajo el capó? Lo pregunto porque actualmente tenemos problemas con las conexiones en CLOSE_WAIT y otros efectos y estamos luchando para encontrar una buena manera de ver qué está sucediendo exactamente.
Christoph
@WandMaker estoy bastante seguro de que simplemente llamaría a shutdown cuando el programa salga o cuando haya terminado con un lote de trabajo en el que no necesitará ninguna conexión durante algún tiempo.
Nicholas DiPiazza
1
@Christoph netstathace un buen trabajo en eso. technet.microsoft.com/en-us/sysinternals/bb897437.aspx también
Nicholas DiPiazza
13

Mi lectura de los documentos es que HttpConnection en sí no se trata como seguro para subprocesos y, por lo tanto, MultiThreadedHttpConnectionManager proporciona un grupo reutilizable de HttpConnections, tiene un único MultiThreadedHttpConnectionManager compartido por todos los subprocesos y se inicializa exactamente una vez. Entonces necesita un par de pequeños refinamientos a la opción A.

MultiThreadedHttpConnectionManager connman = new MultiThreadedHttpConnectionManag

Luego, cada hilo debe usar la secuencia para cada solicitud, obtener una conexión del grupo y volver a colocarla al finalizar su trabajo; usar un bloque final puede ser bueno. También debe codificar la posibilidad de que el grupo no tenga conexiones disponibles y procesar la excepción de tiempo de espera.

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

Como está utilizando un grupo de conexiones, en realidad no cerrará las conexiones y, por lo tanto, esto no debería afectar al problema TIME_WAIT. Este enfoque supone que cada hilo no se aferra a la conexión por mucho tiempo. Tenga en cuenta que el propio estafador se deja abierto.

djna
fuente
No respondí realmente a mi pregunta, sobre qué método (A o B) es mejor.
Cheok Yan Cheng
5

Creo que querrás usar ThreadSafeClientConnManager.

Puedes ver cómo funciona aquí: http://foo.jasonhudgins.com/2009/08/http-connection-reuse-in-android.html

O en el AndroidHttpClientque lo usa internamente.

Thomas Ahle
fuente
1
Opps. No hay un plan para migrar de HttpClient 3.x a 4.x, 3.x como había estado funcionando sin defectos en mi aplicación durante casi 2 años ~ :)
Cheok Yan Cheng
9
Claro, solo si alguien más vino buscando en Google una respuesta :)
Thomas Ahle
4

Con HttpClient 4.5 puede hacer esto:

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

Tenga en cuenta que este implementa Closeable (para cerrar el administrador de conexiones).

Dimitar II
fuente