HttpRequest.execute()を使用した例外:SingleClientConnManagerの無効な使用:接続はまだ割り当てられています


81

google-api-client-java 1.2.1-alphaを使用してPOSTリクエストを実行していますが、HttpRequestをexecute()すると、次のスタックトレースが取得されます。

これは、前のPOSTから同じURLへの403エラーをキャッチして無視し、後続のリクエストでトランスポートを再利用した直後に発生します。(同じATOMフィードに複数のエントリを挿入するループになっています)。

403の後に「クリーンアップ」するためにすべきことはありますか?

Exception in thread "main" java.lang.IllegalStateException: Invalid use of SingleClientConnManager: connection still allocated.
Make sure to release the connection before allocating another one.
    at org.apache.http.impl.conn.SingleClientConnManager.getConnection(SingleClientConnManager.java:199)
    at org.apache.http.impl.conn.SingleClientConnManager$1.getConnection(SingleClientConnManager.java:173)
    at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:390)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:641)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:576)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:554)
    at com.google.api.client.apache.ApacheHttpRequest.execute(ApacheHttpRequest.java:47)
    at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:207)
    at au.com.machaira.pss.gape.RedirectHandler.execute(RedirectHandler.java:38)
    at au.com.machaira.pss.gape.ss.model.records.TableEntry.executeModification(TableEntry.java:81)

私の下のコードが新しい接続を取得しようとしているのはなぜですか?


これはまだバージョン1.11.0-betaの問題のようです:/
sjngm 2012

5
回答を消費しようとすると、まだ警告を取得した後、ここに到着誰の利益のために-私はここで正しい答えを見つけました:tech.chitgoks.com/2011/05/05/...
Steelight

@ Steelight-tech.chitgoks.comアプローチを使用すると、問題が解決しました。
Cale Sweeney 2017年

回答:


82

接続を別のリクエストに再利用する前に、レスポンスの本文を使用する必要があります。応答ステータスを読み取るだけでなくInputStream、最後のバイトまで応答を完全に読み取る必要があります。これにより、読み取られたバイトは無視されます。


1
以上です!の場合google-api-java-client、これは、IOExceptionスローされたものをキャッチしHttpResponse.execute()、テスト/キャストしHttpResponseExceptionresponseメンバーにアクセスして、それを呼び出すことparaseAsString()を意味しました。(とにかく有用な情報であることが判明し
David Bullock 2011年

5
コンテンツを破棄するためのHttpEntity.consumeContent()メソッドもあります。
Grzegorz Adam Hankiewicz 2012

3
EntityUtils.consume(entity)-consumContentは非推奨になりました。
David Carboni 2012年

Google Http Java Clientの最近のバージョン(少なくとも1.16、場合によってはそれ以前)の時点でHttpRequest.execute()は、メソッドがHttpResponseオブジェクトを返さない場合、呼び出しによってこれらのリソースが自動的にクリーンアップされることに注意してください。さらに、HttpResponse現在のJavaDocはresponse.disconnect()、HTTP応答の内容を(finallyブロック内で)完全に読み取らない場合に呼び出すことを推奨しています。
David Bullock

apacheのBasicClientConnectionManagerを内部的に使用するRestEasy3.0.4と同じ問題-httpclient4.2.1。ありがとう@BalusC。また、ストリームを完全に読むことについては何も読んでいません。これらの落とし穴に気付くには、どこを見ればよいでしょうか。(ドキュメントに関して、ソースコード以外)
ダッシュボード

42

JettyでHttpClientを使用してテストフレームワークを構築するときに、同様の問題に直面していました。クライアントからサーブレットへの複数のリクエストを作成する必要がありましたが、実行時に同じ例外が発生していました。

http://foo.jasonhudgins.com/2010/03/http-connections-revisited.htmlで代替案を見つけました

この次の方法を使用して、クライアントをインスタンス化することもできます。

public static DefaultHttpClient getThreadSafeClient()  {

    DefaultHttpClient client = new DefaultHttpClient();
    ClientConnectionManager mgr = client.getConnectionManager();
    HttpParams params = client.getParams();
    client = new DefaultHttpClient(new ThreadSafeClientConnManager(params, 

            mgr.getSchemeRegistry()), params);
    return client;
}

おかげで、かなりうまくいきました。ただし、2つのDefaultHttpClientを作成する必要があることに興味があります
htafoya 2012年

2
彼は、独自のHttpParamsを最初から作成する代わりに、デフォルトのHttpParams(クライアントから取得)を使用します。
Marcin Gil

これで私の問題は解決しましたが、Androidアプリケーションでこれを使用しても問題ありません。
マニッシュ2014年

興味深いことに、これは部分的には機能しますが、ループ内で複数のHTTP要求を行い、応答を消費しない場合、残りのコードの実行をブロックすることになります。また、失敗もありませんでしたが、タイムアウトエラーが発生するかどうかを確認するのにそれほど長くはかかりませんでした。代わりに、リクエストごとに新しいDefaultHttpClientsを割り当てるルートをたどりましたが、ループをあまり長く実行しないので、それでうまくいきました。
デビッド

9

同様の例外メッセージ(少なくともApache Jarkata Commons HTTPクライアント4.2以降)は次のとおりです。

java.lang.IllegalStateException: Invalid use of BasicClientConnManager: connection still allocated. Make sure to release the connection before allocating another one.

この例外は、2つ以上のスレッドが1つのスレッドと相互作用する場合に発生する可能性がありますorg.apache.http.impl.client.DefaultHttpClient

4.2DefaultHttpClientインスタンスをスレッドセーフにする方法(2つ以上のスレッドが上記のエラーメッセージを表示せずにスレッドセーフと対話できるという意味でスレッドセーフ)。!の形式でDefaultHttpClient接続プールを提供します。ClientConnectionManagerorg.apache.http.impl.conn.PoolingClientConnectionManager

/* using
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.2.2</version>
    </dependency>
*/

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.PoolingClientConnectionManager;
import org.apache.http.impl.conn.SchemeRegistryFactory;
import org.apache.http.params.HttpParams;
import org.apache.http.client.methods.HttpGet;

public class MyComponent {

    private HttpClient client;

    {
        PoolingClientConnectionManager conMan = new PoolingClientConnectionManager( SchemeRegistryFactory.createDefault() );
        conMan.setMaxTotal(200);
        conMan.setDefaultMaxPerRoute(200);

        client = new DefaultHttpClient(conMan);

        //The following parameter configurations are not
        //neccessary for this example, but they show how
        //to further tweak the HttpClient
        HttpParams params = client.getParams();
        HttpConnectionParams.setConnectionTimeout(params, 20000);
        HttpConnectionParams.setSoTimeout(params, 15000);
    }


    //This method can be called concurrently by several threads
    private InputStream getResource(String uri) {
        try {
            HttpGet method = new HttpGet(uri);
            HttpResponse httpResponse = client.execute(method);
            int statusCode = httpResponse.getStatusLine().getStatusCode();
            InputStream is = null;
            if (HttpStatus.SC_OK == statusCode) {
                logger.debug("200 OK Amazon request");
                is = httpResponse.getEntity().getContent();
            } else {
                logger.debug("Something went wrong, statusCode is {}",
                        statusCode);
                 EntityUtils.consume(httpResponse.getEntity());
            }
            return is;
        } catch (Exception e) {
            logger.error("Something went terribly wrong", e);
            throw new RuntimeException(e);
        }
    }
}

8

これはよくある質問です。BalusCの応答は正しいです。HttpReponseExceptionをキャッチし、HttpResponseExceptionを呼び出してください。応答無視()。エラーメッセージを読む必要がある場合は、応答を使用してください。応答コンテンツタイプがわからない場合はparseAsString()、それ以外の場合はコンテンツタイプがわかっている場合はresponseを使用します。parseAs(MyType.class)。

簡単なコードスニペットYouTubeSample.javaユーチューブ-jsoncサンプル(通常は、実際のアプリケーションでスマートに何かをしたいと思うが):

  } catch (HttpResponseException e) {
    System.err.println(e.response.parseAsString());
  }

完全な開示:私はgoogle-api-java-clientプロジェクトの所有者です。


15
これがよくある質問であるという事実は、ライブラリの設計にユーザビリティの問題があることを示唆していませんか?
正気2011年

@sanityまたはhttpスタック自体にあるかもしれません;)
krosenvold 2011年

3

単体テストのjax-rs(resteasy)Responseオブジェクトでも同じ問題が発生しました。response.releaseConnection(); releaseConnection()の呼び出しでこれを解決しました-メソッドはresteasyClientResponseオブジェクトにのみあるため、からResponseへのキャストを追加する必要がありましたClientResponse


それは私の日を救った!私の場合、さらに絞り込むためにorg.jboss.resteasy.client.jaxrs.internal.ClientResponseにキャストする必要がありました。
user20812 7919年

1

これを試して

HttpResponse response = Client.execute(httpGet);
response.getEntity().consumeContent();
StatusLine statusLine = response.getStatusLine();
int statusCode = statusLine.getStatusCode();
if (statusCode == 200) {
        //task
    Log.i("Connection", "OK");
    }else{
     Log.i("Connection", "Down");
    }

0

わかりました、私は同様の問題を抱えています、それらの解決策はすべて機能しません、私はいくつかのデバイスでテストしました、問題はデバイスの日付でした、それは2013年ではなく2011年でした、これも役立つことを確認してください。


0

次のようにInputStreamを読み取ります。

if( response.getStatusLine().getStatusCode() == 200 ) {
    HttpEntity entity = response.getEntity();
    InputStream content = entity.getContent();
    try {
        sb = new StringBuilder();
        BufferedReader bufferedReader = new BufferedReader( new InputStreamReader( content ), 8 );
        String line;
        while( ( line = bufferedReader.readLine() ) != null ) {
            sb.append( line );
        }
        bufferedReader.close();
        content.close();
    } catch( Exception ex ) {
        Log.e( "statusCode", ex.getMessage() + "" );
    }
}

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.