Исключение с использованием 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) - consumerContent теперь устарел.
Дэвид Карбони

Просто обратите внимание, что в последних версиях 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.
Manish

Интересно, что это частично работает для меня, заканчивая тем, что блокирует выполнение оставшегося кода, когда я делаю несколько HTTP-запросов в цикле и не использую ответы. И никаких сбоев, я не стал слишком долго ждать, чтобы увидеть, возникает ли ошибка тайм-аута. Поэтому вместо этого я пошел по пути выделения новых DefaultHttpClients для каждого запроса, и это сработало для меня, поскольку я не запускаю цикл очень долго.
Дэвид

9

Аналогичное сообщение об исключении (по крайней мере, поскольку HTTP-клиент Apache Jarkata Commons 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 потокобезопасным ( потокобезопасным в том смысле, что два или более потока могут взаимодействовать с ним, не получая вышеуказанного сообщения об ошибке)? Обеспечьте 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

У меня была такая же проблема с объектом jax-rs (resteasy) Responseв моих модульных тестах. Я решил это с помощью вызова response.releaseConnection(); ReleaseConnection () - Method только для 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() + "" );
    }
}

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.