在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);
方法A由httpclient开发者社区推荐。
请参阅http://www.mail-archive.com/httpclient-users@hc.apache.org/msg02455.html了解更多详情。;
我读到的文档是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理器)。