HttpClient多线程性能

时间:2012-05-21 09:24:57

标签: java multithreading apache-httpclient-4.x

我有一个应用程序,使用HttpClient (4.1.3 or 4.2-beta)从62个目标主机下载超过4500个html页面。它运行在Windows 7 64位上。处理器 - 酷睿i7 2600K。网络带宽 - 54 Mb / s。

此时它使用了这样的参数:

  • DefaultHttpClientPoolingClientConnectionManager;
  • 它还有IdleConnectionMonitorThread http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html;
  • 最大总连接数= 80;
  • 每条路线的默认最大连接数= 5;
  • 对于线程管理,它使用具有并行性的ForkJoinPool 等级= 5(我是否正确理解它是一些工作
    线程?)

在这种情况下,我的网络使用率(在Windows任务管理器中)不会超过2.5%。要下载4500页,需要70分钟。在HttpClient日志中,我有这样的东西:

  

DEBUG ForkJoinPool-2-worker-1   [org.apache.http.impl.conn.PoolingClientConnectionManager]:连接   发布:[id:209] [route:{} - > http://stackoverflow.com] [保留总数   活着:6;分配的路线:1的5;总分配:80的10]

总分配连接不会超过10-12,尽管我已将其设置为80个连接。 如果我尝试将并行度级别提升到20或80,网络使用率仍然相同,但会产生很多连接超时。

我已经阅读过关于hc.apache.org(HttpClient Performance Optimization GuideHttpClient Threading Guide)的教程,但是他们没有帮助。

任务代码如下所示:

public class ContentDownloader extends RecursiveAction {
    private final HttpClient httpClient;
    private final HttpContext context;
    private List<Entry> entries;

    public ContentDownloader(HttpClient httpClient, List<Entry> entries){
        this.httpClient = httpClient;
        context = new BasicHttpContext();
        this.entries = entries;
    }

    private void computeDirectly(Entry entry){      
        final HttpGet get = new HttpGet(entry.getLink());
        try {
            HttpResponse response = httpClient.execute(get, context);
            int statusCode = response.getStatusLine().getStatusCode();

            if ( (statusCode >= 400) && (statusCode <= 600) ) {
                logger.error("Couldn't get content from " + get.getURI().toString() + "\n"  + response.toString());
            } else {        
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    String htmlContent = EntityUtils.toString(entity).trim();
                    entry.setHtml(htmlContent);
                    EntityUtils.consumeQuietly(entity);                             
                }
            }                           
        } catch (Exception e) {
        } finally {
            get.releaseConnection();
        }
    }

    @Override
    protected void compute() {
        if (entries.size() <= 1){           
            computeDirectly(entries.get(0));
            return;         
        }       
        int split = entries.size() / 2;     
        invokeAll(new ContentDownloader(httpClient, entries.subList(0, split)), 
                new ContentDownloader(httpClient, entries.subList(split, entries.size())));
    }
}

问题是 - 使用多线程HttpClient的最佳做法是什么,可能有一些设置ConnectionManagerHttpClient的规则?如何使用所有80个连接并提高网络使用率?

如有必要,我会提供更多代码。

3 个答案:

答案 0 :(得分:4)

我不确定你有多少不同的主机,但如果它是一个小数字(或只是1),你想增加每条路线的最大值。这将增加每个主机的并发性。

目前您已将其设置为5.您正在观察最多10-12的连接使用率,也许您只能使用2-3个不同的主机,在这种情况下,数学会加起来。

答案 1 :(得分:1)

远程站点可以限制来自一个IP的并行连接数。事实上,这是一种很好的做法,因为许多爬虫的实施都很糟糕,并且会给服务器带来很大的负担。

如果你抓取一个公共网站而不是你自己的公共网站,你至少应该尊重robots.txt,并将你的请求限制为每个远程ip每秒一个。

除此之外,每条路线的最大连接数(即http://www.example.com/ [whatever])为5,因此您可以预期最多有5个并行连接到一个遥控器“现场”。 (路径被忽略,只是方案,主机和端口。)

答案 2 :(得分:0)

Apache HttpClient肯定足够快,甚至可以使环路带宽饱和。我怀疑性能问题与内容处理的效率有关,而不是内容检索的效率。您的应用程序只需花费更多时间处理HTML内容并提取链接,而不是下载新页面,从而导致带宽利用不足。即使您的代码在处理之前将HTML内容转换为String这一事实也让我相信您的应用程序花费更多时间在内存中复制内容而不是通过网络传输数据。