在多线程环境中使用HttpClient的最佳实践


84

一段时间以来,我一直在多线程环境中使用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();
}

我们计划拥有:

[方法A]

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

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

在正常情况下,将由50个以上的++线程同时访问global_c。我想知道,这会造成性能问题吗?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)更好?

Answers:


46

绝对是方法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);

49
在4.2中,不推荐使用ThreadSafeClientConnManager,而建议使用PoolingClientConnManager
Drew Stephens

嗨,可以使用此方法创建的httpclient用来维护会话,如此处stackoverflow.com/questions/5960832 / ...所示?因为当我尝试时,我无法维持不同请求之间的会话...
sakthig

17
4.3.1此处:不推荐使用PoolingClientConnManager,而推荐使用PoolingHttpClientConnectionManager。
Matthias 2014年

@DrewStephens再一次不推荐使用PoolingClientConnManager,而推荐使用PoolingHttpClientConnectionManager
didxga

18

httpclient开发人员社区推荐方法A。

有关更多详细信息,请参考http://www.mail-archive.com/httpclient-users@hc.apache.org/msg02455.html


1
如果客户端成为全局客户端,何时会在连接管理器上调用“关机”。
Wand Maker

1
哪些工具/ Linux命令对于调试或“可视化” ConnectionManager的行为很有用?我问是因为我们目前在CLOSE_WAIT中的连接和其他效果方面存在麻烦,并且我们正在努力寻找一种很好的方法来查看实际情况。
克里斯多夫(Christoph)

@WandMaker我很确定您会在程序退出时或完成某些工作而在一段时间内不需要任何连接时才调用shutdown。
尼古拉斯·迪广场

1
@Christophnetstat在这方面做得非常好。technet.microsoft.com/en-us/sysinternals/bb897437.aspx
尼古拉斯迪皮亚扎

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本身保持打开状态。


我的问题不是实际答案,哪种方法(A或B)更好。
Cheok Yan Cheng,


4

使用HttpClient 4.5,您可以执行以下操作:

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.