最近在Ubuntu下配置好了nutch和solr的环境,也用nutch爬取了一些网页,通过solr界面呈现,也过了一把自己建立小搜索引擎的瘾,现在该静下心来好好看看nutch的源码了,先从Inject开始吧~~~
1.从crawl.java的main函数进入,执行: Configuration conf = NutchConfiguration.create();
再进入NutchConfiguration(NutchConfiguration负责加载管理nutch的配置文件信息,该类继承自Configuration,继承了Configuration所有功能,加载配置文件功能也是继承自Configuration类)类的create方法,执行:Configuration conf = new Configuration();从执行结果可以看出在类Configuration的静态代码块中代码 addDefaultResource("core-default.xml");addDefaultResource("core-site.xml");可以看出是加载了core-default.xml, core-site.xml两个文件;
2.通过Configuration完成指定配置文件的加载,加载后的conf为:Configuration: core-default.xml, core-site.xml, nutch-default.xml, nutch-site.xml共四个配置文件;
之后回到crawl的main函数中执行:int res = ToolRunner.run(conf, new Crawl(), args);参数conf是刚刚加载好配置文件的Configuration对象,Crawl是要执行的对象类,args是用户输入的参数串,这里是使用hadoop的工具类ToolRunner运行实例Crawl类,即进入核心的爬虫模块;
然后进入ToolRunner类的run()方法,第一步就是利用类GenericOptionsParser(备注:GenericOptionsParser是hadoop框架中解析命令行参数的基本类。它能够辨别一些标准的命令行参数,能够使应用程序轻易地指定namenode,jobtracker,以及其他额外的配置资源)对输入的命令行参数args(如[urls, -dir, crawl20140724, -depth, 2, -threads, 8, -topN, 100])进行解析,再执行:String[] toolArgs = parser.getRemainingArgs();获得解析后的参数数组(比如[urls, -dir, crawl20140724, -depth, 2, -threads, 8, -topN, 100]);
然后执行返回语句:return tool.run(toolArgs);这里的tool就是Crawl类的对象,run()方法也是Crawl类中的run方法;
3.解析完用户的输入参数后,执行Crawl类的run()方法。第一步就是配置程序的默认参数,如果用户没有相应的参数赋值就使用这些默认的参数。注意这里有一点编程思想可以借鉴:
int threads = getConf().getInt("fetcher.threads.fetch", 10);该行代码的getInt方法具体如下:
public int getInt(String name, int defaultValue) {
String valueString = get(name);//get的核心代码为:return substituteVars(getProps().getProperty(name));,主要是从配置文件中查找有没有相应的赋值
if (valueString == null)
return defaultValue;//如果配置文件中没有赋值,则valueString为null,这是getInt方法的返回值就是默认值10,即defaultValue
try {
String hexString = getHexDigits(valueString);
if (hexString != null) {
return Integer.parseInt(hexString, 16);
}
return Integer.parseInt(valueString);
} catch (NumberFormatException e) {
return defaultValue;
}
}
后面就是遍历args参数串,如果发现匹配的则对相应的变量赋值
第二步初始化jobconf:JobConf job = new NutchJob(getConf());执行后job为Configuration: core-default.xml, core-site.xml, mapred-default.xml, mapred-site.xml, nutch-default.xml, nutch-site.xml等于讲conf的加入后还在JobConf类中的静态代码块中加入了配置文件mapred-default.xml, mapred-site.xml。
后面又初始化文件系统:FileSystem fs = FileSystem.get(job);下面是网上关于FileSystem的介绍(备注:
就像上节所说的,有时候我们无法通过设置URLStreamHandlerFactory方法的方式来通过URL读取数据,这时FIleSystem API就派上用场了。
Hadoop文件系统中的文件是用Hadoop的Path对象来表示的(而不是java中的java.io.File对象,因为它的语义太接近于本地文件系统了)。你可以把一个Path对象看做Hadoop文件系统中的某一个URL,如上例中的“hdfs://localhost/user/tom/quangle.txt”。
Filesystem是一个通用的文件系统API,所以使用它的第一步就是先抽取出它的一个实例出来——在这个例子中是HDFS。下面列出了几个Filesystem的用于抽取Filesystem实例的几个静态方法:
public static FileSystem get(Configuration conf) throws IOException public static FileSystem get(URI uri, Configuration conf) throws IOException public static FileSystem get(URI uri, Configuration conf, String user) throws IOException |
一个Configuration对象封装了客户端或服务器端的配置信息,这些配置信息是通过从conf/core-size.xml之类的配置文件中读取出来的名值对来设置的。下面我们一一说明上面的三个方法:
1)第一个方法返回一个默认的文件系统(在conf/core-site.xml中通过fs.default.name来指定的,如果在conf/core-site.xml中没有设置则返回本地文件系统)。
2)第二个方法通过uri来指定要返回的文件系统(例如,如果uri是上个测试例子中的hdfs://localhost/user/tom/quangle.txt,也即以hdfs标识开头,那么就返回一个hdfs文件系统,如果uri中没有相应的标识则返回本地文件系统)。
3)第三个方法返回文件系统的机理同(2)是相同的,但它同时又限定了该文件系统的用户,这在安全方面是很重要的。
)
4.初始化jobconf和filesystem后,主要是一些参数的界面输出,以及明确临时文件的存放位置并初始化nutch爬取的几个流程类inject、generate、fetch、parse和update等;
然后执行:injector.inject(crawlDb, rootUrlDir);初始化CrawlDb,实现将url转换为指定格式的输入;
进入该方法后,设置一些的参数,包括输入输出,mapper类等,最后提交job交由mapreduce执行,JobClient.runJob(sortJob);
通过调试模式看到,程序是先进入了Inject类的configurable方法进行了一些参数的赋值,然后跳到Inject类的map方法,该方法主要包括:读取url,对url进行分割,url规范化和过滤的操作,然后返回一个处理后的url;
然后再生声明CrawlDatum类:CrawlDatum datum = new CrawlDatum(CrawlDatum.STATUS_INJECTED, customInterval);,最终是要将输入数据转换为<Text,CrawlDatum >格式的,设置一些url的初始化数据(在ScroingFilters类中的injectedScore方法可以看到的datum值为:
Version: 7
Status: 66 (injected)
Fetch time: Thu Jul 24 23:04:37 CST 2014
Modified time: Thu Jan 01 08:00:00 CST 1970
Retries since fetch: 0
Retry interval: 2592000 seconds (30 days)
Score: 1.0
Signature: null
Metadata:
)
通过调试看到程序执行完Inject的map方法后会跳到MapRunner中的run方法,部分代码如下:
public void run(RecordReader<K1, V1> input, OutputCollector<K2, V2> output,
Reporter reporter)
throws IOException {
try {
// allocate key & value instances that are re-used for all entries
K1 key = input.createKey();
V1 value = http://www.mamicode.com/input.createValue();
while (input.next(key, value)) {
……
其中in就是url存放的文件的读入流;
然后又跳入LoadJobRunner类中执行代码,在这里会执行:ReduceTask reduce =
new ReduceTask(systemJobFile.toString(), reduceId, 0, mapIds.size(),
1);……reduce.run(localConf, this);……的代码
之后会有一些输入输出流的关闭以及杀死一些线程,还有删除本地的一些临时文件
以上便是nutch的Inject部分的实现流程。
参考博文:http://blog.csdn.net/amuseme_lu/article/details/6713386