BasicClientConnManager的使用无效:连接仍然被分配。

47

我正在调用REST URL,并尝试测量获取响应所需的时间。

我正在使用DefaultHttpClientREST URL获取响应。

在我的以下程序中,每个线程将在特定范围内工作。例如:每个线程将在1-100之间工作,第二个线程将在101-200之间工作,等等。

因此,在我的以下代码中,第一次正常工作。但是第二次,在这一行httpclient.execute处会抛出异常-

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

我在这里做错了什么吗?-

以下是我的代码-

class Task implements Runnable {

    private DefaultHttpClient httpclient = new DefaultHttpClient();
    private HttpGet httpGet;
    private HttpResponse response;

    @Override
    public void run() {

        try {

            httpGet = new HttpGet(
                    "http://localhost:8080/service/BEService/v1/get/USERID=10000/profile.ACCOUNT.SERVICE
            httpGet.getRequestLine();

            for (int userId = id; userId < id + noOfTasks; userId++) {

                    long start = System.nanoTime();

                    response = httpclient.execute(httpGet);

                    long end = System.nanoTime() - start;
                }
        } catch (Exception e) {
            LOG.error("Threw a Exception in " + getClass().getSimpleName(), e);
        }
    }
}

更新的代码:

如果我像这样做-

class Task implements Runnable {

    private DefaultHttpClient httpclient = new DefaultHttpClient();
    private HttpGet httpGet;
    private HttpResponse response;

    @Override
    public void run() {

        try {

            for (int userId = id; userId < id + noOfTasks; userId++) {

                httpGet = new HttpGet("http://localhost:8080/service/BEService/v1/get/USERID=10000/profile.ACCOUNT.SERVICE");
                httpGet.getRequestLine();

                long start = System.nanoTime();

                response = httpclient.execute(httpGet);

                long end = System.nanoTime() - start;

                HttpEntity entity = response.getEntity();
                EntityUtils.consume(entity);
                }
        } catch (Exception e) {
            LOG.error("Threw a Exception in " + getClass().getSimpleName(), e);
        }
    }
}

那么这样可以吗?

3个回答

46
这里我有做错的地方吗?
是的。如文档中所述:
BasicClientConnectionManager是一个简单的连接管理器,一次只维护一个连接。尽管这个类是线程安全的,但它应该只由一个执行线程使用。BasicClientConnectionManager将努力重用与相同路线的后续请求的连接。然而,如果持久连接的路线不匹配于连接请求的路线,则会关闭现有连接并重新打开它。如果已经分配了连接,那么就会抛出java.lang.IllegalStateException异常。
HttpClient默认使用BasicClientConnectionManager。
请参见“多线程请求执行”,了解如何使用池化连接管理器来处理跨多个线程的请求。

感谢Ryan的建议。我已经根据更改更新了代码。如果我这样做,会有任何问题吗? - AKIWEB
1
从你展示的片段来看很难说。听起来你有一个HttpClient跨线程,这在基本连接管理器中是不允许的。我想第一种情况可能是你没有正确地释放低级资源 - Ryan Stewart
请纠正我如果我错了,但在他的代码中httpClient没有被共享。在C#中,线程是一个方法,但在Java中它是整个类(jj09.net/multithreading-csharp-vs-java)。所以问题确实是资源释放(https://www.securecoding.cert.org/confluence/x/9gFqAQ,https://hc.apache.org/httpcomponents-core-ga/httpcore/apidocs/org/apache/http/util/EntityUtils.html#consume(org.apache.http.HttpEntity))。 - Gabe
@RyanStewart 这些链接已经失效了。 - Nathan

44
假设您正在使用原始的DefaultHttpClient(它在内部使用BasicClientConnectionManager),您首先需要消耗未完成/最后的响应。

EntityUtils.consumeQuietly(httpResponse.getEntity());

否则,您可以每次重新分配DefaultHttpClient
否则,您可以将execute()加上EntityUtils.consumeQuietly()包装在synchronized()块中,以防止多线程错误。
来源:解决方案-在使用后不需要每次关闭DefaultHttpClient()

1
首先确保只有一个线程使用DefaultHttpClient;然后对于每个execute,不要忘记httpResponse.getEntity()。这样就可以了。 - Codefor
1
第一个建议很有力。第二个建议不是很好,因为重新分配Http客户端实例并不便宜。 - asgs

1

这是我使用连接池管理器配置RestTemplate的方式。它在5个或更多并发线程中表现非常出色。

<!-- RestTemplate -->
<beans:bean id="restTemplateYT" class="org.springframework.web.client.RestTemplate">
    <beans:constructor-arg ref="httpRequestFactoryYT" />
</beans:bean>

<beans:bean id="httpRequestFactoryYT" class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory"> 
    <beans:constructor-arg>
        <beans:bean class="org.apache.http.impl.client.DefaultHttpClient">
            <beans:constructor-arg>
                <beans:bean class="org.apache.http.impl.conn.PoolingClientConnectionManager"/>
            </beans:constructor-arg>
        </beans:bean>
    </beans:constructor-arg>
    <beans:property name="connectTimeout" value="5000" />
</beans:bean>

Spring 版本: 3.1.0


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接