使用 HttpRequest.execute() 时出现异常:SingleClientConnManager 的使用无效:连接仍然分配

发布于 2024-10-10 07:01:33 字数 1410 浏览 7 评论 0原文

我正在使用 google-api-client-java 1.2.1-alpha 执行 POST 请求,并且当我执行() HttpRequest 时得到以下堆栈跟踪。

在我捕获并忽略上一个 POST 到同一 URL 的 403 错误并重新使用后续请求的传输后,就会立即发生这种情况。 (它在一个循环中将多个条目插入到同一个 ATOM feed 中)。

出现 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)

为什么我下面的代码会尝试获取连接?

I'm using google-api-client-java 1.2.1-alpha to execute a POST request, and am getting the following stacktrace when I execute() the HttpRequest.

It happens immediately after I catch and ignore a 403 error from a previous POST to the same URL, and re-used the transport for the subsequent request. (It's in a loop inserting multiple entries to the same ATOM feed).

Is there something I should be doing to 'clean up' after a 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)

Why would the code below me be trying to acquire a new connection?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

玩物 2024-10-17 07:01:33

您需要先使用响应正文,然后才能将连接重用于另一个请求。您不仅应该读取响应状态,还应该完全读取响应 InputStream 直到最后一个字节,从而忽略读取的字节。

You need to consume the response body before you can reuse the connection for another request. You should not only read the response status, but read the response InputStream fully to the last byte whereby you just ignore the read bytes.

银河中√捞星星 2024-10-17 07:01:33

在使用 HttpClient 和 Jetty 构建测试框架时,我遇到了类似的问题。我必须从客户端创建多个对 Servlet 的请求,但在执行时它给出了相同的异常。

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

I was facing a similar issue when using the HttpClient with Jetty to build a test framework. I had to create multiple requests to the Servelet from my client, but It was giving the same exception when executed.

I found an alternative at http://foo.jasonhudgins.com/2010/03/http-connections-revisited.html

You can also use this following method to instantiate your client.

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;
}
烂人 2024-10-17 07:01:33

类似的异常消息(至少从 Apache Jarkata Commons HTTP Client 4.2 起)是:

java.lang.IllegalStateException:BasicClientConnManager 的使用无效:连接仍在分配。
确保在分配另一个连接之前释放连接。

当两个或多个线程与单个 org.apache.http.impl.client.DefaultHttpClient 交互时,可能会发生此异常。

如何使 4.2 DefaultHttpClient 实例线程安全(线程安全是指两个或多个线程可以与其交互而不会收到上述错误消息)?以 org.apache.http.impl.conn.PoolingClientConnectionManager 的形式为 DefaultHttpClient 提供连接池 ClientConnectionManager

/* 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);
        }
    }
}

A similar exception message (since at least Apache Jarkata Commons HTTP Client 4.2) is:

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

This exception can happen when two or more threads interact with a single org.apache.http.impl.client.DefaultHttpClient.

How can you make a 4.2 DefaultHttpClient instance threadsafe (threadsafe in the sense that two or more threads can interact with it without getting above error message)? Provide DefaultHttpClient with a connection-pooling ClientConnectionManager in the form of 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);
        }
    }
}
浮华 2024-10-17 07:01:33

这是一个经常被问到的问题。 BalusC的回应是正确的。请抓住 HttpReponseException,并调用 HttpResponseException。响应忽略()。如果您需要阅读错误消息,请使用response。parseAsString() 如果您不知道响应内容类型,否则如果您知道内容类型,请使用响应。parseAs(MyType.class)。

来自 YouTubeSample.java /youtube-jsonc-sample/instructions.html?r=default" rel="noreferrer">youtube-jsonc-sample (尽管通常您会希望在实际应用程序中做一些更聪明的事情):

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

完全披露:我是 google-api-java-client 项目的所有者。

This is an often-asked question. BalusC's response is correct. Please catch HttpReponseException, and call HttpResponseException.response.ignore(). If you need to read the error message, use response.parseAsString() if you don't know the response content type, else if you do know the content type use response.parseAs(MyType.class).

A simple code snippet from YouTubeSample.java in youtube-jsonc-sample (though usually you'll want to do something smarter in a real application):

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

Full disclosure: I am an owner of the google-api-java-client project.

找回味觉 2024-10-17 07:01:33

我在单元测试中使用 jax-rs (resteasy) Response 对象也遇到了同样的问题。
我通过调用response.releaseConnection();解决了这个问题
releaseConnection() 方法仅适用于 Resteasy ClientResponse 对象,因此我必须添加从 ResponseClientResponse 的转换。

I had the same issue with a jax-rs (resteasy) Response object in my unit tests.
I solved this with a call to response.releaseConnection();
The releaseConnection()-Method is only on the resteasy ClientResponse object, so I had to add a cast from Response to ClientResponse.

甜心 2024-10-17 07:01:33

试试这个

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

Try this

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");
    }
如梦亦如幻 2024-10-17 07:01:33

好的,我有类似的问题,所有这些解决方案都不起作用,我在某些设备上进行了测试,问题是设备中的日期,它是 2011 年而不是 2013 年,检查一下这也可以提供帮助。

Ok, i have similar problem, all those solution not work, i tested on some device, problem was date in device, it was 2011 instead 2013, check also this can help.

沉默的熊 2024-10-17 07:01:33

像这样读取输入流:

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

Read the InputStream like this:

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() + "" );
    }
}
烏雲後面有陽光 2024-10-17 07:01:33

只需使用如下所示的响应即可解决问题

response.getEntity().consumeContent();

just consume the response like below, that will solve the issue

response.getEntity().consumeContent();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文