82 votes

Meilleure pratique pour utiliser HttpClient dans un environnement multithreadé

Depuis un certain temps, j'utilise HttpClient dans un environnement multithread. Pour chaque thread, lorsqu'il initie une connexion, il crée une toute nouvelle instance de HttpClient.

Récemment, j'ai découvert qu'en utilisant cette approche, l'utilisateur peut avoir trop de ports ouverts, et la plupart des connexions sont en état TIME_WAIT.

http://www.opensubscriber.com/message/commons-httpclient-dev@jakarta.apache.org/86045.html

Ainsi, au lieu que chaque thread fasse :

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

Nous prévoyons d'avoir :

[MÉTHODE A]

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

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

Dans une situation normale, global_c sera accédé par 50++ threads simultanément. Je me demandais si cela pouvait créer des problèmes de performance. MultiThreadedHttpConnectionManager utilise-t-il un mécanisme sans verrou pour mettre en œuvre sa politique de sécurité des threads ?

Si 10 fils utilisent global_c, les 40 autres fils seront-ils verrouillés ?

Ou serait-il préférable que, dans chaque fil, je crée une instance de HttpClient, mais que je libère explicitement le gestionnaire de connexion ?

[MÉTHODE B]

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

Est-ce que connman.shutdown() aura des problèmes de performance ?

Puis-je savoir quelle méthode (A ou B) est la meilleure, pour une application utilisant un fil 50++ ?

49voto

Définitivement la méthode A parce qu'elle est mise en commun et sans risque pour les fils.

Si vous utilisez httpclient 4.x, le gestionnaire de connexion est appelé ThreadSafeClientConnManager . Voir cette lien pour plus de détails (faites défiler la page jusqu'à "Gestionnaire de connexions en pool"). Par exemple :

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

17voto

Cheok Yan Cheng Points 11825

La méthode A est recommandée par la communauté des développeurs de httpclient.

Veuillez vous référer à http://www.mail-archive.com/httpclient-users@hc.apache.org/msg02455.html pour plus de détails.

13voto

djna Points 34761

D'après ce que j'ai lu dans la documentation, HttpConnection lui-même n'est pas traité comme thread safe, et donc MultiThreadedHttpConnectionManager fournit un pool réutilisable de HttpConnections, vous avez un seul MultiThreadedHttpConnectionManager partagé par tous les threads et initialisé exactement une fois. Il faut donc apporter quelques petites améliorations à l'option A.

MultiThreadedHttpConnectionManager connman = new MultiThreadedHttpConnectionManag

Ensuite, chaque thread devrait utiliser la séquence pour chaque demande, obtenir une conection du pool et la remettre à la fin de son travail - l'utilisation d'un bloc final peut être utile. Vous devez également coder pour le cas où le pool n'a pas de connexions disponibles et traiter l'exception de timeout.

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

Comme vous utilisez un pool de connexions, vous ne fermerez pas réellement les connexions et cela ne devrait donc pas poser le problème de TIME_WAIT. Cette approche suppose que chaque thread ne s'accroche pas longtemps à la connexion. Notez que conman lui-même est laissé ouvert.

7voto

Dimitar II Points 963

Avec HttpClient 4.5, vous pouvez le faire :

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

Notez que celui-ci implémente Closeable (pour la fermeture du gestionnaire de connexion).

5voto

Thomas Ahle Points 10403

Je pense que vous voudrez utiliser ThreadSafeClientConnManager.

Vous pouvez voir comment cela fonctionne ici : http://foo.jasonhudgins.com/2009/08/http-connection-reuse-in-Android.html

Ou dans le AndroidHttpClient qui l'utilise en interne.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X