原文链接:http://www.cnblogs.com/MichaelYin/archive/2011/10/10/2205699.html
实际的url的处理是在toethread中进行的,toethread从Frontier中请求待处理的url,并将其放到一系列Processor中进行处理
可以以流水线上的处理流程来想象Processor,流水线上的产品就是url,由于处理的processor主要着重于不同的阶段和功能,所以,heritrix将其processor分成了五个大类。
1 Pre-fetch
这里面包含一些需要在像web服务器发出请求之前的一些处理的processor,比如检查url的scope
2 Fetch
这里面的是获取web信息的processor,不同的processor支持不同的协议,比如FetchHTTP支持http,FetchDNS支持dns
3 Extractor
这个里面就是涉及到对抓取的web信息进行内容的提取了,可以对其中的链接进行处理,放到以后进行抓取,也可以根据自己的业务逻辑进行相应的扩展
4 Write/index
将抓取的信息存储到硬盘中
5. Post-processing
将Extractor中找到的url放入到Frontier中待以后进行抓取,进行processor的清理工作
所有的处理类都是继承了Processor,这样就能以一定的抽象统一对所有的processor进行管理,而每个具体的processor只需要根据自己的需要重写相应的处理方法即可
CrawlController在setupCrawlModules中会初始化processorChains,ProcessorChainList这个实例就是整个处理器的一个容器,它下面根据上面所说的分类构成了5个ProcessorChain,需要注意的是初始化的时候顺序是很重要的,因为这个顺序关系到url处理时候的顺序。ProcessorChain中包含了所需要的processor
多线程相关
Processor的实例在CrawlController中会存放在ProcessorChain中,如果有特殊需要为了保证每个线程有属于自己的Processor,只需要实现InstancePerThread这个端口即可
00 | private Processor getProcessor(Processor processor) { |
01 |
if (!(processor instanceof InstancePerThread)) { |
02 |
// just use the shared Processor |
03 |
return processor; |
04 |
} |
05 |
// must use local copy of processor |
06 |
Processor localProcessor = (Processor) localProcessors. get ( |
07 |
processor.getClass().getName()); |
08 |
if (localProcessor == null ) { |
09 |
localProcessor = processor.spawn( this .getSerialNumber()); |
10 |
localProcessors.put(processor.getClass().getName(),localProcessor); |
11 |
} |
12 |
return localProcessor; |
13 | } |
这个方法对于需要新建的Processor通过反射重新建立实例并存放在ToeThread的localProcessors中
当时在看一个扩展Processor的时候觉得很奇怪的就是本来下意识的觉得多个线程公用一个东西应该使用同步,所以看到Processor并没有使用同步的关键字觉得很奇怪,后来仔细想了一下觉得这个地方其实是有它的道理的。因为同步是为了什么?为了共享资源访问的时候不出问题,而这里处理url的过程中Processor的处理本来就没什么共享的东西需要进行同步,所以这样设计是没有问题的。这也告诉我们,对于多线程调用的一些方法,同步关键字并不是必须的,同步的本质是对于共享资源访问时的同步