3

我有一个应用程序,它使用HttpClient (4.1.3 or 4.2-beta). 它在 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 日志中,我有这样的事情:

调试 ForkJoinPool-2-worker-1 [org.apache.http.impl.conn.PoolingClientConnectionManager]:连接释放:[id:209][路由:{}->http://stackoverflow.com][总保持活动: 6;分配的路线:5 条中的 1 条;总分配: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可能有一些设置规则吗?如何使用全部 80 个连接并提高网络使用率?ConnectionManagerHttpClient

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

4

3 回答 3

4

我不确定您从多少个不同的主机中提取,但如果它是一个小数字(或只有 1 个),您希望增加每条路由的最大值。这将增加每台主机的并发性。

目前您已将其设置为 5。您观察到最大连接使用量高达 10-12,也许您只访问 2-3 个不同的主机,在这种情况下,数学加起来。

于 2012-09-22T21:16:27.993 回答
1

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

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

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

于 2013-10-16T08:11:52.130 回答
0

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

于 2012-05-21T12:59:55.713 回答