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객체, 내가로부터 캐스트를 추가했다, 그래서 Response에 ClientResponse.


그것은 내 하루를 구했습니다! 제 경우에는 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.