web-dev-qa-db-fra.com

HttpClient 4.0.1 - comment libérer la connexion?

J'ai une boucle sur un tas d'URL, pour chacune d'elles, je fais ce qui suit:

private String doQuery(String url) {

  HttpGet httpGet = new HttpGet(url);
  setDefaultHeaders(httpGet); // static method
  HttpResponse response = httpClient.execute(httpGet);   // httpClient instantiated in constructor

  int rc = response.getStatusLine().getStatusCode();

  if (rc != 200) {
    // some stuff...
    return;
  }

  HttpEntity entity = response.getEntity();

  if (entity == null) {
    // some stuff...
    return;
  }

  // process the entity, get input stream etc

}

La première requête est correcte, la seconde lève cette exception:

Exception dans le fil "main" Java.lang.IllegalStateException: Utilisation non valide de SingleClientConnManager: connexion toujours alloué. Assurez-vous de libérer la connexion avant d’allouer un autre. à org.Apache.http.impl.conn.SingleClientConnManager.getConnection (SingleClientConnManager.Java:199) à org.Apache.http.impl.conn.SingleClientConnManager $ 1.getConnection (SingleClientConnManager.Java:173) ......

Ceci est juste une application simple mono-thread. Comment puis-je libérer cette connexion?

68
Richard H

Pour répondre à ma propre question: pour libérer la connexion (et toutes les autres ressources associées à la demande), vous devez fermer le flux InputStream renvoyé par HttpEntity:

InputStream is = entity.getContent();

.... process the input stream ....

is.close();       // releases all resources

De la docs

22
Richard H

Httpcomponents 4.1 recommande de fermer la connexion et de libérer toutes les ressources sous-jacentes:

EntityUtils.consume(HttpEntity)

HttpEntity transmis est une entité de réponse.

92
Yadu

Cela semble bien fonctionner:

      if( response.getEntity() != null ) {
         response.getEntity().consumeContent();
      }//if

Et n'oubliez pas de consommer l'entité même si vous n'avez pas ouvert son contenu. Par exemple, vous attendez un statut HTTP_OK de la réponse et vous ne l'obtenez pas, vous devez toujours utiliser l'entité!

31
Snicolas

Depuis la version 4.2, ils ont introduit une méthode beaucoup plus pratique qui simplifie la publication de connexion: HttpRequestBase.releaseConnection ()

18
wikier

J'interviens avec une réponse détaillée qui aborde spécifiquement Apache HttpClient 4.0.1. J'utilise cette version de HttpClient, car elle est fournie par WAS v8.0 et que je dois utiliser celle fournie par HttpClient dans Apache Wink v1.1.1, également fournie par WAS v8.0, pour créer une authentification NTLM REST. ] appels à Sharepoint.

Pour citer Oleg Kalnichevski sur la liste de diffusion Apache HttpClient:

Presque tout ce code n'est pas nécessaire. (1) HttpClient sera libérer automatiquement la connexion sous-jacente tant que l'entité le contenu est consommé jusqu'à la fin du flux; (2) HttpClient volonté Libérez automatiquement la connexion sous-jacente pour toute exception d'E/S jeté en lisant le contenu de la réponse. Aucun traitement spécial n'est requis dans tel cas.

En fait, cela est parfaitement suffisant pour assurer une libération appropriée des ressources:

HttpResponse rsp = httpclient.execute(target, req); 
HttpEntity entity = rsp.getEntity(); 
if (entity != null) {
     InputStream instream = entity.getContent();
     try {
         // process content
     } finally {
         instream.close();
         // entity.consumeContent() would also do
     } 
}

C'est ça.

La source

12
Chris Harris

Si la réponse ne doit pas être consommée, la demande peut être annulée à l'aide du code ci-dessous:

// Low level resources should be released before initiating a new request
HttpEntity entity = response.getEntity();

if (entity != null) {
    // Do not need the rest
    httpPost.abort();
}

Référence: http://hc.Apache.org/httpcomponents-client-ga/tutorial/html/fundamentals.html#d5e143

Apache HttpClient Version: 4.1.3

7
Jignesh Gohel

J'ai ce problème lorsque j'utilise HttpClient dans un environnement multithread (Servlets). Un servlet maintient toujours la connexion et un autre veut établir la connexion. 

Solution:  

version 4.0 utiliser ThreadSafeClientConnManager

version 4.2 utilise PoolingClientConnectionManager

et mettre ceci deux setters: 

setDefaultMaxPerRoute
setMaxTotal
4
chris

Les demandes HTTP HEAD doivent être traitées légèrement différemment car response.getEntity () est null . Vous devez au contraire capturer le HttpContext transmis dans HttpClient.execute () et récupérer le paramètre de connexion pour le fermer (dans HttpComponents 4.1 .X en tout cas).

HttpRequest httpRqst = new HttpHead( uri );
HttpContext httpContext = httpFactory.createContext();
HttpResponse httpResp = httpClient.execute( httpRqst, httpContext );

...

// Close when finished
HttpEntity entity = httpResp.getEntity();
if( null != entity )
  // Handles standard 'GET' case
  EntityUtils.consume( entity );
else {
  ConnectionReleaseTrigger  conn =
      (ConnectionReleaseTrigger) httpContext.getAttribute( ExecutionContext.HTTP_CONNECTION );
  // Handles 'HEAD' where entity is not returned
  if( null != conn )
    conn.releaseConnection();
}

HttpComponents 4.2.X a ajouté un releaseConnection () à HttpRequestBase pour rendre cela plus facile.

2
wbdarby

J'utilise HttpClient 4.5.3, en utilisant CloseableHttpClient#close a fonctionné pour moi.

    CloseableHttpResponse response = client.execute(request);

    try {
        HttpEntity entity = response.getEntity();
        String body = EntityUtils.toString(entity);
        checkResult(body);
        EntityUtils.consume(entity);
    } finally {
        response.close();
    }
1
Fan Jin

Si vous souhaitez réutiliser la connexion, vous devez utiliser le flux de contenu complètement après chaque utilisation, comme suit: 

EntityUtils.consume(response.getEntity())

Remarque: vous devez utiliser le flux de contenu même si le code d'état n'est pas 200. Si vous ne le faites pas, les problèmes suivants seront soulevés lors de la prochaine utilisation: 

Exception dans le thread "principal" Java.lang.IllegalStateException: Utilisation non valide de SingleClientConnManager: connexion toujours allouée. Assurez-vous de libérer la connexion avant d'en allouer une autre. 

S'il s'agit d'une utilisation ponctuelle, la simple fermeture de la connexion libérera toutes les ressources qui lui sont associées. 

1
Ayman Hussain

Je recommande fortement d'utiliser un gestionnaire pour gérer la réponse.

client.execute(yourRequest,defaultHanler);

Il libérera automatiquement la connexion avec la méthode consume(HTTPENTITY).

Un exemple de gestionnaire:

private ResponseHandler<String> defaultHandler = new ResponseHandler<String>() {
    @Override
    public String handleResponse(HttpResponse response)
        throws IOException {
        int status = response.getStatusLine().getStatusCode();

        if (status >= 200 && status < 300) {
            HttpEntity entity = response.getEntity();
            return entity != null ? EntityUtils.toString(entity) : null;
        } else {
            throw new ClientProtocolException("Unexpected response status: " + status);
        }
    }
};
0
Lincoln

J'avais le même problème et je l'ai résolu en fermant la réponse à la fin de la méthode:

try {
    // make the request and get the entity 
} catch(final Exception e) {
    // handle the exception
} finally {
    if(response != null) {
        response.close();
    }
}
0
kerl