在multithreading环境中使用HttpClient的最佳实践

有一段时间,我一直在multithreading环境中使用HttpClient。 对于每个线程,当它启动连接时,它将创build一个全新的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(); } 

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

如果10个线程正在使用global_c,其他40个线程是否会被locking?

或者,如果在每个线程中创build一个HttpClient实例,但明确释放连接pipe理器,会更好吗?

[方法B]

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

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

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

绝对方法A,因为它的汇集和线程安全。

如果您使用httpclient 4.x,连接pipe理器被称为ThreadSafeClientConnManager 。 请参阅此链接获取更多详细信息(向下滚动到“连接pipe理器”)。 例如:

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

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

 MultiThreadedHttpConnectionManager connman = new MultiThreadedHttpConnectionManag 

然后,每个线程都应该使用顺序来处理每个请求,从池中获得一个连接,并在其工作完成时重新进行连接 – 使用finally块可能会很好。 您还应该编写池的可用连接并处理超时exception的可能性。

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

由于您使用的是连接池,因此实际上并不会closures连接,因此这不会触及TIME_WAIT问题。 这种方法假定每个线程都不会长时间挂在连接上。 请注意,conman本身是打开的。

我想你会想使用ThreadSafeClientConnManager。

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

或者在内部使用它的AndroidHttpClient中。

用HttpClient 4.5你可以这样做:

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

请注意,这个实现了Closeable(closures连接pipe理器)。