Виняток із використанням HttpRequest.execute (): Недійсне використання SingleClientConnManager: підключення все-таки виділено


81

Я використовую google-api-client-java 1.2.1-alpha для виконання запиту POST, і я отримую такий стек-трас, коли я виконую () HttpRequest.

Це відбувається відразу після того, як я виявляю та ігнорую помилку 403 із попереднього POST на ту саму URL-адресу та повторно використовую транспорт для наступного запиту. (Це в циклі, який вставляє кілька записів до одного і того ж каналу 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

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

3
EntityUtils.consume (entity) - consumeContent тепер застарілий.
Девід Карбоні,

Тільки зауважимо, що станом на останні версії Google Http Java Client (принаймні 1,16, але можливо і раніше), виклик HttpRequest.execute()автоматично очистить ці ресурси, якщо метод не зможе повернути HttpResponseоб’єкт. Крім того, JavaDoc HttpResponseвідтепер рекомендує телефонувати response.disconnect()у випадку не повного читання вмісту відповіді HTTP (у finallyблоці).
Девід Баллок,

Та сама проблема з RestEasy 3.0.4, який внутрішньо використовує BasicClientConnectionManager з apache-httpclient 4.2.1. Дякую @BalusC. Крім того, я ніколи не читав нічого про повне читання потоку, куди слід дивитись, коли йдеться про усвідомлення цих підводних каменів? (документально, крім вихідного коду)
alegria

42

Я стикався з подібною проблемою, коли використовував HttpClient з Jetty для побудови тестової основи. Мені довелося створити кілька запитів до сервелета від мого клієнта, але він давав такий самий виняток при виконанні.

Я знайшов альтернативу на 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

Це вирішило мою проблему, але чи нормально використовувати це в додатку для Android.
маніш

Цікаво, що це частково працює для мене, в кінцевому підсумку блокує виконання коду, що залишився, коли я роблю кілька запитів 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.

Як ви можете створити DefaultHttpClientпримірник потоків 4.2 ( threadsafe в тому сенсі, що два або більше потоків можуть взаємодіяти з ним, не отримуючи повідомлення про помилку)? Надайте 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 у youtube-jsonc-sample (хоча зазвичай вам захочеться зробити щось розумніше у реальному додатку):

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

Повне розкриття інформації: Я є власником проекту google-api-java-client .


15
Чи не свідчить той факт, що це часто задане запитання, про те, що у дизайні вашої бібліотеки є проблема юзабіліті?
осудність

@sanity Або, можливо, в самому стеку http;)
krosenvold

3

У мене була та ж проблема з Responseоб'єктом jax-rs (resteasy) у моїх модульних тестах. Я вирішив це за допомогою виклику response.releaseConnection(); ReleConnection () - Метод застосовується лише до ClientResponseоб'єкта resteasy , тому мені довелося додати приклад від 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

Гаразд, у мене схожа проблема, всі ці рішення не працюють, я тестував на якомусь пристрої, проблема була датою на пристрої, це був 2011 рік замість 2013 року, перевірте також, це може допомогти.


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

0

просто споживайте відповідь, як показано нижче, яка вирішить проблему

response.getEntity().consumeContent();
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.