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-

5
응답을 시도한 후에도 여전히 경고를받은 후 여기에 도착한 사람을 위해-정답을 찾았습니다. tech.chitgoks.com/2011/05/05/…
Steelight

@ Steelight-tech.chitgoks.com 접근 방식을 사용하여 문제가 해결되었습니다.
케일 스위니

답변:


82

다른 요청에 연결을 재사용하려면 먼저 응답 본문을 사용해야합니다. 응답 상태를 읽을뿐만 아니라 읽은 InputStream바이트를 무시하는 마지막 바이트까지 응답을 완전히 읽어야합니다.


1
그거였다! 의 경우 google-api-java-client이것은에서 IOException던진 것을 잡아서으로 HttpResponse.execute()테스트 / 캐스트 HttpResponseException하고 response멤버에 액세스 한 다음 호출 paraseAsString()하는 것을 의미합니다. (어느 어쨌든로 유용한 정보를 판명 : -)
데이비드 불락

5
콘텐츠를 삭제하는 HttpEntity.consumeContent () 메서드도 있습니다.
Grzegorz Adam Hankiewicz 2011

3
EntityUtils.consume (entity)-ConsumeContent는 이제 더 이상 사용되지 않습니다.
David Carboni

최신 버전의 Google Http Java Client (최소 1.16 이상 가능하지만 이전 버전) HttpRequest.execute()에서 메서드가 HttpResponse객체 를 반환하지 못하는 경우 호출 은 이러한 리소스를 자동으로 정리 합니다. 또한 JavaDoc은 HTTP 응답의 내용 ( 블록 단위)을 완전히 읽지 못하는 경우 HttpResponse호출 할 것을 권장 합니다. response.disconnect()finally
David Bullock

apache-httpclient 4.2.1의 BasicClientConnectionManager를 내부적으로 사용하는 RestEasy 3.0.4와 동일한 문제입니다. @BalusC 감사합니다. 또한 나는 스트림을 완전히 읽는 것에 대해 아무것도 읽지 않았습니다. 이러한 함정을 인식하려면 어디를보아야합니까? (문서 측면, 소스 코드 제외)
대시 보드

42

Jetty와 함께 HttpClient를 사용하여 테스트 프레임 워크를 구축 할 때 비슷한 문제에 직면했습니다. 클라이언트에서 Servelet에 여러 요청을 작성해야했지만 실행시 동일한 예외가 발생했습니다.

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

고마워, 꽤 잘 했어. 내가 필요성에 궁금하지만 두 DefaultHttpClients을 만들 수 있습니다
htafoya

2
그는 처음부터 직접 생성하는 대신 기본 HttpParams (클라이언트에서 가져옴)를 사용합니다.
Marcin Gil

그것은 내 문제를 해결했지만 안드로이드 응용 프로그램에서 이것을 사용하는 것이 좋습니다.
manish

흥미롭게도 부분적으로 저에게 효과가 있으며 루프에서 여러 HTTP 요청을 만들고 응답을 사용하지 않을 때 나머지 코드가 실행되지 않도록 차단합니다. 그리고 실패도 없었지만 시간 초과 오류가 발생하는지 너무 오래 기다리지 않았습니다. 그래서 대신 각 요청에 대해 새 DefaultHttpClients를 할당하는 경로를 갔고 루프를 매우 오래 실행하지 않기 때문에 저에게 효과적이었습니다.
데이비드

9

유사한 예외 메시지 (적어도 Apache Jarkata Commons HTTP Client 4.2 이후)는 다음과 같습니다.

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

이 예외는 둘 이상의 스레드가 단일 org.apache.http.impl.client.DefaultHttpClient.

어떻게 당신은 4.2 만들 수 있습니다 DefaultHttpClient(인스턴스 스레드 스레드 두 개 이상의 스레드가 오류 메시지가 위받지 않고 상호 작용할 수 있다는 의미에서)을? 형식으로 DefaultHttpClient연결 풀링 ClientConnectionManager을 제공하십시오 org.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 (), 내용 유형을 알고있는 경우 응답을 사용합니다. parseAs (MyType.class).

에서 간단한 코드 조각 YouTubeSample.java 에서 유튜브 - jsonc 샘플 (일반적으로 비록 당신이 실제 응용 프로그램에서 스마트 무언가를 할 수 있습니다)

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

전체 공개 : 저는 google-api-java-client 프로젝트 의 소유자입니다 .


15
이것이 자주 묻는 질문이라는 사실이 라이브러리 디자인에 사용성 문제가 있음을 시사하지 않습니까?
sanity

는 HTTP 스택 자체 @sanity 아니면)
krosenvold

3

Response내 단위 테스트에서 jax-rs (resteasy) 개체 와 동일한 문제가 발생했습니다 . 나는에 대한 호출을 통해이 문제를 해결 response.releaseConnection(); releaseConnection () - 방법 만 RESTEasy가의에 ClientResponse객체, 내가로부터 캐스트를 추가했다, 그래서 ResponseClientResponse.


그것은 내 하루를 구했습니다! 제 경우에는 org.jboss.resteasy.client.jaxrs.internal.ClientResponse로 캐스팅하여 더 좁혀 야했습니다.
user2081279

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.