Nutch FetchData作业太慢

时间:2019-08-22 11:42:48

标签: hadoop mapreduce web-crawler amazon-emr nutch

我正在使用Apache Nutch在EMR集群中以6个周期编程地爬行大约7000个URL(在爬行过程中很少有自定义map-reduce作业)。 版本是:nutch = v1.15 hadoop = 2.7.3 我正在具有20个EC2 m4.large现货实例的Amazon EMR集群上运行它。进行爬网的代码是:

    public crawl(Folder seeds, Folder output) 
        throws IOException, InterruptedException {
        final Folder crawldb = output.folder("crawldb");
        try {
            new Injector(this.conf).inject(
                crawldb.path(), seeds.path(),
                true, true
            );
        } catch (final ClassNotFoundException err) {
            throw new IOException("Failed to inject URLs", err);
        }
        final Folder segments = output.mkdir("segments");
        // cycles = 6 in my case
        for (int idx = 0; idx < cycles; ++idx) {
            this.cycle(crawldb, segments);
        }
    }

    private void cycle(final Folder crawldb, final Folder segments)
        throws IOException, InterruptedException {
        try {
            Logger.info(this, "Generating...");
            // configured as 1_000_000 in EMR cluster
            final int topn = this.conf.getInt("yc.gen.topn", 1000);
            // configured as 40 (2 x slave_nodes) in EMR cluster
            final int nfetch = this.conf.getInt("yc.gen.nfetch", 1);
            new Generator(this.conf).generate(
                crawldb.path(),
                segments.path(),
                nfetch, topn, System.currentTimeMillis()
            );
            // the latest segment
            final Optional<Folder> next = Batch.nextSegment(segments);
            if (next.isPresent()) {
                final Path sgmt = next.get().path();
                Logger.info(this, "Fetching %s...", sgmt);
                new Fetcher(this.conf).fetch(
                    // @checkstyle MagicNumber (1 line)
                    sgmt, 10
                );
                Logger.info(this, "Parsing %s...", sgmt);
                new ParseSegment(this.conf).parse(sgmt);
            }
            new CrawlDb(this.conf).update(
                crawldb.path(),
                // all segments paths
                segments.subfolders().stream()
                    .toArray(Path[]::new),
                true, true
            );
        } catch (final ClassNotFoundException err) {
            throw new IOException(
                "Failed to generate/fetch/parse segment", err
            );
        }
    }

当我使用7000个种子URL和6个运行周期运行它时,Nutch在FetchData上的工作变得非常缓慢:它运行了大约3个小时,似乎正在等待最后一个映射器完成大约2.5最后几个小时(请参见所附屏幕截图)。这项工作可能是什么问题,以及如何加快FetchData阶段,也许我可以将其配置为跳过缓慢的访存程序(如果我错过了几个URL,这不是什么大问题)。 job mappers

1 个答案:

答案 0 :(得分:2)

Nutch的生成器作业按主机将获取列表划分为多个队列(或者,域,请参见partition.url.mode)。在一个提取程序映射任务中处理一个提取队列的所有URL,以确保礼貌性约束-随时只有一个主机与一个主机的单个连接,并且对同一主机的请求之间有一定的延迟。分区对于性能也很重要,因为可以在地图任务中本地完成DNS解析,robots.txt解析和结果缓存。

如果一个或几个获取队列过长,或者很少爬行的主机响应太慢,则这些队列会“阻止”爬行进度。为解决此问题,可以将三个选项组合使用:

  1. 使用属性fetcher.timelimit.mins限制允许提取程序映射任务运行的时间。如果未达到时间限制,则会在下一个周期中跳过并提取来自提取队列的其余URL。
  2. 使用generate.max.countgenerate.count.mode确保没有队列变得太大
  3. (仅允许您对具有更激进设置的 all 个主机进行爬网),您可以使用更短的爬网延迟(fetcher.server.delay)甚至允许并行连接({{1} })

还有更多选项可以调整爬网的性能,所有属性都记录在文件fetcher.threads.per.queue中。默认值可以很好地确保限制在一组主机/域上的爬网的完整性,并且需要更改默认值才能在广泛的爬网中获得较高的吞吐量,因为在广泛的爬网中,某些主机/域无法彻底爬网。