web-dev-qa-db-ja.com

HttpClient 4.0.1-接続を解放する方法は?

私は次のことをしているURLごとに、たくさんのURLをループしています。

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

}

最初のクエリは問題なく、2番目のクエリはこの例外をスローします。

スレッド「メイン」の例外Java.lang.IllegalStateException:SingleClientConnManagerの無効な使用:接続はまだ割り当てられています。別の接続を割り当てる前に、接続を必ず解放してください。 org.Apache.http.impl.conn.SingleClientConnManager.getConnection(SingleClientConnManager.Java:199)at org.Apache.http.impl.conn.SingleClientConnManager $ 1.getConnection(SingleClientConnManager.Java:173)......

これは単純なシングルスレッドアプリです。この接続を解除するにはどうすればよいですか?

70
Richard H

私自身の質問に答えるには、接続(およびリクエストに関連付けられた他のリソース)を解放するには、HttpEntityによって返されたInputStreamを閉じる必要があります。

InputStream is = entity.getContent();

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

is.close();       // releases all resources

docs から

23
Richard H

Httpcomponents 4.1で推奨される方法は、接続を閉じて、基になるリソースを解放することです。

EntityUtils.consume(HttpEntity)

ここで、渡されるHttpEntityは応答エンティティです。

95
Yadu

これはうまくいくようです:

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

また、コンテンツを開かなかった場合でも、エンティティを使用することを忘れないでください。たとえば、応答からHTTP_OKステータスを期待し、それを取得しない場合、まだエンティティを消費する必要があります!

32
Snicolas

バージョン4.2以降、接続リリースを簡素化するはるかに便利なメソッドが導入されました。 HttpRequestBase.releaseConnection()

19
wikier

私は、Apache HttpClient 4.0.1に具体的に対処する詳細な回答を探しています。このHttpClientバージョンを使用しているのは、WAS v8.0によって提供されているためです。また、NTLM認証REST Sharepointを呼び出します。

Apache HttpClientメーリングリストでOleg Kalnichevskiを引用するには:

このコードはほとんど必要ありません。 (1)HttpClientは、エンティティコンテンツがストリームの最後まで消費される限り、基になる接続を自動的に解放します。 (2)HttpClientは、応答コンテンツの読み取り中にスローされたI/O例外の基になる接続を自動的に解放します。このような場合、特別な処理は必要ありません。

実際、これはリソースの適切なリリースを保証するために完全に十分です:

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
     } 
}

それだ。

ソース

12
Chris Harris

応答が消費されない場合、以下のコードを使用して要求を中止できます。

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

参照: http://hc.Apache.org/httpcomponents-client-ga/tutorial/html/fundamentals.html#d5e14

Apache HttpClientバージョン:4.1.3

7
Jignesh Gohel

マルチスレッド環境(サーブレット)でHttpClientを使用すると、この問題が発生します。 1つのサーブレットがまだ接続を保持しており、別のサーブレットが接続を取得したい。

解決策:

バージョン4.0はThreadSafeClientConnManagerを使用します

バージョン4.2はPoolingClientConnectionManagerを使用します

この2つのセッターを設定します。

setDefaultMaxPerRoute
setMaxTotal
4
chris

Response.getEntity()はnullであるため、HTTP HEADリクエストは少し異なる方法で処理する必要があります。代わりに、HttpClient.execute()に渡されたHttpContextをキャプチャし、接続パラメーターを取得して閉じます(とにかくHttpComponents 4.1.Xで)。

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では、これを簡単にするためにHttpRequestBaseにreleaseConnection()が追加されました。

2
wbdarby

私はCloseableHttpClient#closeを使用してHttpClient 4.5.3を使用しています。

    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

接続を再利用する場合は、次のように使用するたびにコンテンツストリームを完全に消費する必要があります。

EntityUtils.consume(response.getEntity())

注:ステータスコードが200でない場合でも、コンテンツストリームを消費する必要があります。そうしないと、次の使用時に次のメッセージが表示されます。

スレッド「メイン」の例外Java.lang.IllegalStateException:SingleClientConnManagerの無効な使用:接続はまだ割り当てられています。別の接続を割り当てる前に、接続を必ず解放してください。

一度だけ使用する場合は、接続を閉じるだけで、それに関連付けられているすべてのリソースが解放されます。

1
Ayman Hussain

ハンドラーを使用して応答を処理することを強くお勧めします。

client.execute(yourRequest,defaultHanler);

consume(HTTPENTITY)メソッドで接続を自動的に解放します。

ハンドラーの例:

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

私は同じ問題を抱えていて、メソッドの最後で応答を閉じることで解決しました:

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