92

一段时间以来,我一直在多线程环境中使用 HttpClient。对于每个线程,当它发起一个连接时,它会创建一个全新的 HttpClient 实例。

最近发现,使用这种方式会导致用户打开的端口过多,大部分连接都处于TIME_WAIT状态。

http://www.opensubscriber.com/message/commons-httpclient-dev@jakarta.apache.org/86045.html

因此,不是每个线程都在做:

HttpClient c = new HttpClient();
try {
    c.executeMethod(method);
}
catch(...) {
}
finally {
    method.releaseConnection();
}

我们计划拥有:

[方法一]

// global_c is initialized once through
// HttpClient global_c = new HttpClient(new MultiThreadedHttpConnectionManager());

try {
    global_c.executeMethod(method);
}
catch(...) {
}
finally {
    method.releaseConnection();
}

在正常情况下,global_c 将被 50++ 个线程同时访问。我想知道,这会产生任何性能问题吗?MultiThreadedHttpConnectionManager 是否使用无锁机制来实现其线程安全策略?

如果有 10 个线程在使用 global_c,其他 40 个线程会被锁定吗?

或者,如果在每个线程中创建一个 HttpClient 的实例,但显式释放连接管理器会更好吗?

[方法B]

MultiThreadedHttpConnectionManager connman = new MultiThreadedHttpConnectionManager();
HttpClient c = new HttpClient(connman);
try {
      c.executeMethod(method);
}
catch(...) {
}
finally {
    method.releaseConnection();
    connman.shutdown();
}

connman.shutdown() 会遇到性能问题吗?

对于使用 50++ 线程的应用程序,我可以知道哪种方法(A 或 B)更好吗?

4

5 回答 5

49

绝对是方法 A,因为它的池化和线程安全。

如果您使用的是 httpclient 4.x,则连接管理器称为ThreadSafeClientConnManager。有关更多详细信息,请参阅此链接(向下滚动到“池连接管理器”)。例如:

    HttpParams params = new BasicHttpParams();
    SchemeRegistry registry = new SchemeRegistry();
    registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
    ClientConnectionManager cm = new ThreadSafeClientConnManager(params, registry);
    HttpClient client = new DefaultHttpClient(cm, params);
于 2010-09-23T21:11:33.077 回答
18

方法 A 是 httpclient 开发者社区推荐的。

请参阅http://www.mail-archive.com/httpclient-users@hc.apache.org/msg02455.html了解更多详情。

于 2009-08-29T15:35:56.087 回答
13

我对文档的阅读是 HttpConnection 本身不被视为线程安全的,因此 MultiThreadedHttpConnectionManager 提供了一个可重用的 HttpConnections 池,您有一个由所有线程共享的 MultiThreadedHttpConnectionManager 并且只初始化一次。因此,您需要对选项 A 进行一些小的改进。

MultiThreadedHttpConnectionManager connman = new MultiThreadedHttpConnectionManag

然后每个线程应该为每个请求使用序列,从池中获取连接并在完成工作时将其放回 - 使用 finally 块可能会很好。您还应该对池没有可用连接的可能性进行编码并处理超时异常。

HttpConnection connection = null
try {
    connection = connman.getConnectionWithTimeout(
                        HostConfiguration hostConfiguration, long timeout) 
    // work
} catch (/*etc*/) {/*etc*/} finally{
    if ( connection != null )
        connman.releaseConnection(connection);
}

当您使用连接池时,您实际上不会关闭连接,因此这不应该遇到 TIME_WAIT 问题。这种方法确实假设每个线程不会长时间挂在连接上。请注意,conman 本身是打开的。

于 2009-08-15T06:09:17.257 回答
7

使用 HttpClient 4.5,您可以这样做:

CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(new PoolingHttpClientConnectionManager()).build();

请注意,这实现了 Closeable (用于关闭连接管理器)。

于 2017-10-09T20:00:00.740 回答
5

我想你会想要使用 ThreadSafeClientConnManager。

你可以在这里看到它是如何工作的:http: //foo.jasonhudgins.com/2009/08/http-connection-reuse-in-android.html

或者在AndroidHttpClient内部使用它。

于 2010-08-13T22:42:08.770 回答