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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1281219/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 07:45:10  来源:igfitidea点击:

Best Practice to Use HttpClient in Multithreaded Environment

javaapache-commons-httpclient

提问by Cheok Yan Cheng

For a while, I have been using HttpClient in a multithreaded environment. For every thread, when it initiates a connection, it will create a completely new HttpClient instance.

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

Recently, I have discovered that, by using this approach, it can cause the user to have too many ports being opened, and most of the connections are in TIME_WAIT state.

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

http://www.opensubscriber.com/message/[email protected]/86045.html

http://www.opensubscriber.com/message/[email protected]/86045.html

Hence, instead of each thread doing :

因此,而不是每个线程做:

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

We plan to have :

我们计划有:

[METHOD A]

【方法一】

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

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

In a normal situation, global_c will be accessed by 50++ threads concurrently. I was wondering, will this create any performance issues? Is MultiThreadedHttpConnectionManager using a lock-free mechanism to implement its thread safe policy?

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

If 10 threads are using global_c, will the other 40 threads be locked?

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

Or would it be better if, in every thread, I create an instance of an HttpClient, but release the connection manager explicitly?

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

[METHOD B]

[方法B]

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

Will connman.shutdown() suffer performance issues?

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

May I know which method (A or B) is better, for application using an 50++ threads?

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

采纳答案by Cheok Yan Cheng

Method A is recommended by httpclient developer community.

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

Please refer http://www.mail-archive.com/[email protected]/msg02455.htmlfor more details.

请参阅http://www.mail-archive.com/[email protected]/msg02455.html了解更多详情。

回答by djna

My reading of the docs is that HttpConnection itself is not treated as thread safe, and hence MultiThreadedHttpConnectionManager provides a reusable pool of HttpConnections, you have a single MultiThreadedHttpConnectionManager shared by all threads and initialised exactly once. So you need a couple of small refinements to option A.

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

MultiThreadedHttpConnectionManager connman = new MultiThreadedHttpConnectionManag

Then each thread should be using the sequence for every request, getting a conection from the pool and putting it back on completion of its work - using a finally block may be good. You should also code for the possibility that the pool has no available connections and process the timeout exception.

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

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

As you are using a pool of connections you won't actually be closing the connections and so this should not hit the TIME_WAIT problem. This approach does assuume that each thread doesn't hang on to the connection for long. Note that conman itself is left open.

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

回答by Thomas Ahle

I think you will want to use ThreadSafeClientConnManager.

我想你会想要使用 ThreadSafeClientConnManager。

You can see how it works here: http://foo.jasonhudgins.com/2009/08/http-connection-reuse-in-android.html

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

Or in the AndroidHttpClientwhich uses it internally.

或者在AndroidHttpClient内部使用它。

回答by Thomas Ahle

Definitely Method A because its pooled and thread safe.

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

If you are using httpclient 4.x, the connection manager is called ThreadSafeClientConnManager. See this linkfor further details (scroll down to "Pooling connection manager"). For example:

如果您使用的是 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);

回答by Dimitar II

With HttpClient 4.5 you can do this:

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

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

Note that this one implements Closeable (for shutting down of the connection manager).

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