Heritrix的架构之CrawController

CrawlController即中央控制器,是一次抓取任务中的核心组件。它将决定整个抓取任务的开始和结束CrawlController位于org.archive.crawler.framework中,在它的Field声明中,看到如下代码片段。
//  key subcomponents which define and implement a crawl in progress 
private   transient  CrawlOrder order; 
private   transient  CrawlScope scope; 
private   transient  ProcessorChainList processorChains; 
 
private   transient  Frontier frontier; 
 
private   transient  ToePool toePool; 
 
private   transient  ServerCache serverCache; 
 
//  This gets passed into the initialize method. 
private   transient  SettingsHandler settingsHandler; 

注:这里transient是用于声明序列化的时候不被存储的。

可以看到,在CrawlController类中,定义了以下几个组件: 
 CrawlOrder:这就不用说了,因为一个抓取工作必须要有一个Order对象,它保存了对该次抓取任务中order.xml的属性配置。
CrawlScope:这是决定当前的抓取范围的一个组件。
ProcessorChainList:从名称上很明显就能看出,它表示了处理器链。 
 Frontier:很明显,一次抓取任务需要设定一个Frontier,以此来不断为其每个线程提供URI。
ToePool:这是一个线程池,它管理了所有该抓取任务所创建的子线程。
ServerCache:这是一个缓存,它保存了所有在当前任务中,抓取过的Host名称和Server名称。

以上组件应该是一次正常的抓取过程中所必需的几项,它们各自的任务很独立,分工明确,但在后台中,它们之间却有着千丝万缕的联系,彼此互相做为构造函数或初始化的参数传入。 那么,究竟该如何获CrawlController的实例,并且通过自主的编程来使用Heritrix提供的API进行一次抓任务呢? 事实上CrawlController有一个不带参数的构造函数,开发者可以直接通过它的构造函数来构造一个CrawlController的实例。但是值得注意的一点,在构造一个实例并进行抓取任务时,有几个步骤需要完成:
(1)首先构造一个XMLSettingsHandler对象,将order.xml内的属性信息装入。
(2)调用CrawlController的构造函数,构造一个CrawlController的实例。
(3)调用CrawlController的intialize(SettingsHandler)方法,初始化CrawlController实例。其中,传入的参数  是在第一步是构造的XMLSettingsHandler实例。
(4)当上述3步完成后,CrawlController就已经具备运行的条件,可以开始运行了。此时,只需调用它的requestCrawlStart()方法,就可以启运线程池和Frontier,然后就可以开始不断的抓取网页了。

在CrawlController的initialize()方法中,Heritrix主要做了以下几件事:
(1)从XMLSettingsHandler中取出Order。
(2)检查了用户设定的UserAgent等信息,看是否符合格式。
(3)设定了开始抓取后保存文件信息的目录结构。
(4)初始化了日志信息的记录工具。
(5)初始化了使用Berkley DB的一些工具。
(6)初始化了Scope、Frontier以及ProcessorChain。
(7)最后实例化了线程池。

在正常情况下,以上顺序不能够被随意变动,因为后一项功能的初始化很有可能需要前几项功能初始化的结果。例如线程池的初始化,必须要在先有了Frontier的实例的基础上来进行。

最终启动抓取工作的是requestCrawlStart()方法。其代码如下。

public   void  requestCrawlStart() { 
 
//  初始化处理器链 
 runProcessorInitialTasks(); 
 
 
//  设置一下抓取状态的改变,以便能够激发一些Listeners 
 
//  来处理相应的事件 
 sendCrawlStateChangeEvent(STARTED, CrawlJob.STATUS_PENDING); 
 String jobState; 
 state 
=  RUNNING; 
 jobState 
=  CrawlJob.STATUS_RUNNING; 
 sendCrawlStateChangeEvent(
this .state, jobState); 
 
 
//  A proper exit will change this value. 
  this .sExit  =  CrawlJob.STATUS_FINISHED_ABNORMAL; 
 
 
//  开始日志线程 
 Thread statLogger  =   new  Thread(statistics); 
 statLogger.setName(
" StatLogger " ); 
 statLogger.start(); 
 
 
//  启运Frontier,抓取工作开始 
 frontier.start(); 

可以看到,启动抓取工作的核心就是要启动Frontier(通过调用其start()方法),以便能够开始向线程池中的工作线程提供URI,供它们抓取。

 下面的代码就是BdbFrontier的父类AbstractFrontier中的start()方法和unpause()方法:

public   void  start() { 
 
if  (((Boolean)getUncheckedAttribute( null , ATTR_PAUSE_AT_START)) 
.booleanValue()) { 
 
//  若配置文件中不允许该次抓取开始 
 
//  则停止 
  controller.requestCrawlPause(); 
 } 
else  { 
 
//  若允许开始,则开始 
  unpause(); 
 } 

 

 

synchronized   public   void  unpause() { 
 
//  去除当前阻塞变量 
 shouldPause  =   false
 
//  唤醒所有阻塞线程,开始抓取任务 
 notifyAll(); 

在start()方法中,首先判断配置中的属性是否允许当前线程开始。若不允许,则令controller停止抓取。若允许开始,则简单的调用unpause()方法。unpause()方法更为简单,它首先将阻塞线程的信号量设为false,即允许线程开始活动,然后通过notifyAll()方法,唤醒线程池中所有被阻塞的线程,开始抓取。

注意: notifyAll()的作用就是唤醒所有正在等待该对象的线程。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值