Nutch FetchData 作业太慢
Nutch FetchData job is too slow
我正在使用 Apache Nutch 以编程方式在 EMR 集群中以 6 个周期抓取大约 7000 个 URL(抓取过程中很少有自定义 map-reduce 作业)。
版本是:nutch=v1.15 hadoop=2.7.3
我 运行 将它连接到具有 20 个 EC2 m4.large spot 实例的 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,那不是什么大问题)。
Nutch 的生成器作业按主机(或者域,请参阅 partition.url.mode
)将获取列表划分为队列。一个获取队列的所有 URL 都在一个获取器映射任务中处理,以确保礼貌约束——任何时候只有一个连接到一台主机,并且保证对同一主机的请求之间有延迟。分区对于性能也很重要,因为 DNS 解析、robots.txt 解析和结果缓存可以在地图任务中本地完成。
如果一个或几个抓取队列太长或少数抓取主机响应太慢,这些队列"block"抓取进度。为了克服这个问题,可以组合使用三个选项:
- 使用 属性
fetcher.timelimit.mins
限制 fetcher 映射任务被允许 运行 的时间。如果达到时间限制,将跳过提取队列中剩余的 URL,并在下一个周期中提取。
- 使用
generate.max.count
和 generate.count.mode
确保队列不会变得太大
- (仅当您被允许抓取具有更激进设置的 所有 主机时)您可以使用更短的抓取延迟 (
fetcher.server.delay
) 甚至允许并行连接(fetcher.threads.per.queue
)
有更多选项可以调整爬网性能,所有属性都记录在文件 conf/nutch-default.xml
中。默认值可以很好地确保限制为一组 hosts/domains 的爬网的完整性,并且需要更改以在广泛的爬网中获得高吞吐量,其中接受一些 hosts/domains 无法详尽地爬网.
我正在使用 Apache Nutch 以编程方式在 EMR 集群中以 6 个周期抓取大约 7000 个 URL(抓取过程中很少有自定义 map-reduce 作业)。 版本是:nutch=v1.15 hadoop=2.7.3 我 运行 将它连接到具有 20 个 EC2 m4.large spot 实例的 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,那不是什么大问题)。
Nutch 的生成器作业按主机(或者域,请参阅 partition.url.mode
)将获取列表划分为队列。一个获取队列的所有 URL 都在一个获取器映射任务中处理,以确保礼貌约束——任何时候只有一个连接到一台主机,并且保证对同一主机的请求之间有延迟。分区对于性能也很重要,因为 DNS 解析、robots.txt 解析和结果缓存可以在地图任务中本地完成。
如果一个或几个抓取队列太长或少数抓取主机响应太慢,这些队列"block"抓取进度。为了克服这个问题,可以组合使用三个选项:
- 使用 属性
fetcher.timelimit.mins
限制 fetcher 映射任务被允许 运行 的时间。如果达到时间限制,将跳过提取队列中剩余的 URL,并在下一个周期中提取。 - 使用
generate.max.count
和generate.count.mode
确保队列不会变得太大
- (仅当您被允许抓取具有更激进设置的 所有 主机时)您可以使用更短的抓取延迟 (
fetcher.server.delay
) 甚至允许并行连接(fetcher.threads.per.queue
)
有更多选项可以调整爬网性能,所有属性都记录在文件 conf/nutch-default.xml
中。默认值可以很好地确保限制为一组 hosts/domains 的爬网的完整性,并且需要更改以在广泛的爬网中获得高吞吐量,其中接受一些 hosts/domains 无法详尽地爬网.