以下主要介绍了下generator的第一个MapReduce
首先generate可能主要分成三部分,
- + 第一部分是产生要抓取的url子集,进行相应的过滤和规格化操作
- + 第二部分是读取上面产生的url子集,生成多个segment
- + 第三部分是更新crawldb数据库,以保证下一次Generate不会包含相同的url
第一部分:
// map to inverted subset due for fetch, sort by score
JobConf job = new NutchJob(getConf());
job.setJobName("generate: select from " + dbDir);
// 如果用户没有设置numFetchers这个值,那就默认为Map的个数
if (numLists == -1) { // for politeness make
numLists = job.getNumMapTasks(); // a partition per fetch task
}
// 如果MapReduce的设置为local,那就产生一个输出文件
// NOTE:这里partition也是Hadoop中的一个概念,就是在Map后,它会对每一个key进行partition操作,看这个key会映射到哪一个reduce上,
// 所以相同key的value就会聚合到这个reduce节点上
if ("local".equals(job.get("mapred.job.tracker")) && numLists != 1) {
// override
LOG.info("Generator: jobtracker is 'local', generating exactly one partition.");
numLists = 1;
}
job.setLong(GENERATOR_CUR_TIME, curTime);
// record real generation time
long generateTime = System.currentTimeMillis();
job.setLong(Nutch.GENERATE_TIME_KEY, generateTime);
job.setLong(GENERATOR_TOP_N, topN);
job.setBoolean(GENERATOR_FILTER, filter);
job.setBoolean(GENERATOR_NORMALISE, norm);
job.setInt(GENERATOR_MAX_NUM_SEGMENTS, maxNumSegments);
// 配置输入路径
FileInputFormat.addInputPath(job, new Path(dbDir, CrawlDb.CURRENT_NAME));
job.setInputFormat(SequenceFileInputFormat.class); // 配置CrawlDb的输入格式
// 配置Mapper,Partitioner和Reducer,这里都是Selector,因为它继承了这三个抽象接口
job.setMapperClass(Selector.class);
job.setPartitionerClass(Selector.class);
job.setReducerClass(Selector.class);
FileOutputFormat.setOutputPath(job, tempDir);
// 配置输出格式
job.setOutputFormat(SequenceFileOutputFormat.class);
// 配置输出的<key,value>的类型<FloatWritable,SelectorEntry>
job.setOutputKeyClass(FloatWritable.class);
// 因为Map的输出会按key来排序,所以这里扩展了一个排序比较方法
job.setOutputKeyComparatorClass(DecreasingFloatComparator.class);
job.setOutputValueClass(SelectorEntry.class);
// 设置输出格式,这个类继承自OutputFormat,如果用户要扩展自己的OutputFormat,那必须继承自这个抽象接口
job.setOutputFormat(GeneratorOutputFormat.class);
try {
JobClient.runJob(job); // 提交任务
} catch (IOException e) {
throw e;
}
看一下generator的第一部分的Map做了些什么工作。
public void map(Text key, CrawlDatum value,
OutputCollector<FloatWritable,SelectorEntry> output, Reporter reporter)
throws IOException {
Text url = key;
if (filter) {
// If filtering is on don't generate URLs that don't pass
// URLFilters
try {
//lter the bad urls 符不符合设定的过滤器的要求,可以根据扩展点要求进行过滤。不满足要求就过滤掉
if (filters.filter(url.toString()) == null) return;
} catch (URLFilterException e) {
if (LOG.isWarnEnabled()) {
LOG.warn("Couldn't filter url: " + url + " (" + e.getMessage() + ")");
}
}
}
CrawlDatum crawlDatum = value;
// check fetch schedule 检查该url添加到抓取列表中,根据抓取时间,但是,不一定在列表中就一定抓取
if (!schedule.shouldFetch(url, crawlDatum, curTime)) {
LOG.debug("-shouldFetch rejected '" + url + "', fetchTime="
+ crawlDatum