本博客属原创文章,欢迎转载!转载请务必注明出处:http://guoyunsky.iteye.com/blog/650744

     本博客已迁移到本人独立博客: http://www.yun5u.com/

欢迎加入Heritrix群(QQ):109148319,10447185 , Lucene/Solr群(QQ) :  118972724

上一篇博客主要介绍了CrawlController的各个属性,博客地址:http://guoyunsky.iteye.com/blog/650744 ,以及三个特殊的属性.这里就介绍它的相关方法,首先从初始化开始介绍,主要请看代码以及注释:

1.Heritrix的初始化:

/** * 初始化CrawlController * @param sH 配置文件(order.xml)对象 * @throws InitializationException 初始化异常 */ public void initialize(SettingsHandler sH) throws InitializationException { // 给监听器发送状态为准备状态 sendCrawlStateChangeEvent(PREPARING, CrawlJob.STATUS_PREPARING); // 重入锁,保证只有1个线程使用本对象 this.singleThreadLock = new ReentrantLock(); this.settingsHandler = sH;//order.xml对象 // 工具类方法,把爬虫的SettingsHandler填充进本线程global持有人,如此使得接下来本线程的反序列化操作能够找到它 installThreadContextSettingsHandler(); this.order = settingsHandler.getOrder();//获得order.xml管理对象 this.order.setController(this);//设置order.xml由当前控制中心控制 this.bigmaps = new Hashtable<String, CachedBdbMap<?, ?>>();//初始化备份中心数据装载器 sExit = ""; this.manifest = new StringBuffer();// 初始化所有日志名记录器 String onFailMessage = ""; try { onFailMessage = "You must set the User-Agent and From HTTP" + " header values to acceptable strings. \n" + " User-Agent: [software-name](+[info-url])[misc]\n" + " From: [email-address]\n"; order.checkUserAgentAndFrom();// 检查user-agent,主要通过正则表达式进行验证 onFailMessage = "Unable to setup disk"; if (disk == null) { setupDisk();// 创建logs、states、checkpoint等路径(这里只是创建这几个文件夹) } onFailMessage = "Unable to create log file(s)"; setupLogs();// 创建日志文件,并且设置好各种日志文件格式 onFailMessage = "Unable to test/run checkpoint recover"; this.checkpointRecover = getCheckpointRecover();//获得备份恢复器 if (this.checkpointRecover == null) {//如果备份恢复器为空则新建一个 this.checkpointer = new Checkpointer(this, this.checkpointsDisk); } else { //如果不为空,则先对备份数据进行恢复,主要是构造备份定时器以及填充备份数据到调度中心 setupCheckpointRecover(); } // 创建DBD环境,这里只创建BDB数据库环境 onFailMessage = "Unable to setup bdb environment."; setupBdb(); // 创建跟踪统计器 onFailMessage = "Unable to setup statistics"; setupStatTracking(); // 初始化了Scope、Frontier、ServerCache以及ProcessorChain onFailMessage = "Unable to setup crawl modules"; setupCrawlModules(); } catch (Exception e) { String tmp = "On crawl: " + settingsHandler.getSettingsObject(null).getName() + " " + onFailMessage; //异常日志 LOGGER.log(Level.SEVERE, tmp, e); throw new InitializationException(tmp, e); } // 创建DNS缓存 Lookup.getDefaultCache(DClass.IN).setMaxEntries(1); // 设置线程池,从配置文件获取线程个数后初始化 setupToePool(); // 设置基于crawl order的最大字节数,文档树和时间,这些都是从配置文件中获取 setThresholds(); // 设置应急处理内存,这里是6M reserveMemory = new LinkedList<char[]>(); for (int i = 1; i < RESERVE_BLOCKS; i++) { reserveMemory.add(new char[RESERVE_BLOCK_SIZE]); } }


CrawlController的初始化实际上是创建与之相关的各个不见,如日志文件、BDB、处理器、统计跟踪器等。其中日志文件用于记录各个日志、BDB是个嵌入式数据库用于存放URL以及备份数据、处理器之前的博客有介绍、统计跟踪器则主要用来统计跟踪抓取情况,如多少URL被抓取等。由于这些并不复杂,里就不再陈述...

 2.Heritrix的启动:

/** * 启动Heritrix,开始抓取 */ public void requestCrawlStart() { //初始化各种处理器 runProcessorInitialTasks(); // 将状态置为就绪状态(会发给每一个事件监听器) sendCrawlStateChangeEvent(STARTED, CrawlJob.STATUS_PENDING); String jobState; state = RUNNING; jobState = CrawlJob.STATUS_RUNNING; // 将状态置为正在运行状态(会发给每一个事件监听器,并记录日志) sendCrawlStateChangeEvent(this.state, jobState); //存在状态 this.sExit = CrawlJob.STATUS_FINISHED_ABNORMAL; //初始化统计监听器线程 Thread statLogger = new Thread(statistics); statLogger.setName("StatLogger"); //启动统计监听器 statLogger.start(); //启动调度器 frontier.start(); }


启动Heritrix的代码相对较少.可以看见这里主要是初始化处理器,以及发送状态,同时开启统计监听器线程。最后启动调度器,唤醒所有等待线程。如此开始进入抓取。

3.Heritrix的暂停

/** * Stop the crawl temporarly. * 暂停抓取 */ public synchronized void requestCrawlPause() { if (state == PAUSING || state == PAUSED) { // Already about to pause return; } sExit = CrawlJob.STATUS_WAITING_FOR_PAUSE;// 设置退出状态 frontier.pause(); sendCrawlStateChangeEvent(PAUSING, this.sExit);// 发送状态 if (toePool.getActiveToeCount() == 0) {// 获取出于活动状态的线程数,如果数字为0,则表示暂停结束 // if all threads already held, complete pause now // (no chance to trigger off later held thread) completePause(); } }


可以看到Heritrix的暂停实际上是线程的暂停,不过每一个多线程应用的暂停也都是线程的暂停吧。Heritrix首先暂停调度器,如此使得抓取线程无法获取URL,然后发送暂停命令,使得每个处理器接受到暂停命令后暂停各自的处理工作。最后查看线程池中是否还有活动状态线程,没有的话则表明暂停完成,这些都可以在UI界面中查看到。

4.Heritrix的重启

/** * Resume crawl from paused state * 从暂停状态恢复抓取 */ public synchronized void requestCrawlResume() { if (state != PAUSING && state != PAUSED && state != CHECKPOINTING) {// 不是暂停火车checkpoing状态 // Can't resume if not been told to pause or if we're in middle of // a checkpoint. return; } multiThreadMode();// 回到多线程模式 frontier.unpause();// frontier取消暂停,这意味着重新开始抓取任务 LOGGER.fine("Crawl resumed."); sendCrawlStateChangeEvent(RUNNING, CrawlJob.STATUS_RUNNING);// 发送事件,让所有环节重新启动 }


通过注释可以看到,从暂停状态重启Heritrix实际上是单线程切换到多线程,然后调度器首先要重启,最后也是发送命令给所有处理器让他们重启

   5.Heritrix的停止

   由于Heritrix有两种停止方式,一种是强制终止,一般是接收WEB UI命令后的强行终止抓取,这时的抓取获取并没有抓取完成。还有一种是自然停止,也就是所有的URL都抓取完毕。所以前一种停止是被谋杀,而后一种是寿寝正终。下面先介绍下强制终止:

/** * 开始停止抓取 */ public void beginCrawlStop() { LOGGER.fine("Started."); //发送停止命令 sendCrawlStateChangeEvent(STOPPING, this.sExit); if (this.frontier != null) { this.frontier.terminate(); this.frontier.unpause(); } LOGGER.fine("Finished."); }


可以看到和暂停、重启基本雷同。也是调度器的停止和发送停止命令给各个处理器。只不过这里是先要停止各个处理器,然后再停止调度器。只不过这里后面还多了个调度器的重启,有些让人费解。这里我说下自己的理解,由于Heritrix停止后允许让Heritrix暂停,可以通过配置命令配置,所以这里并没有做到大家想象中的那种停止,而是让Heritrix的调度中心启动在那,而所有的处理模块却都停止了,实际上还是无法进行任何抓取。

   然后是Heritrix的正常停止

/** * Called when the last toethread exits. * 当没有活动状态的线程,则爬虫终止 * */ protected void completeStop() { LOGGER.fine("Entered complete stop."); runProcessorFinalTasks();// 运行所有处理器的最后处理环节 // Ok, now we are ready to exit. sendCrawlStateChangeEvent(FINISHED, this.sExit); // 发送事件表明爬虫结束 synchronized (this.registeredCrawlStatusListeners) { this.registeredCrawlStatusListeners .removeAll(this.registeredCrawlStatusListeners);//移除所有的监听事件 this.registeredCrawlStatusListeners = null; } closeLogFiles();// 关闭所有的日志处理器 // Release reference to logger file handler instances. this.fileHandlers = null; this.uriErrors = null; this.uriProcessing = null; this.localErrors = null; this.runtimeErrors = null; this.progressStats = null; this.reports = null; this.manifest = null; // Do cleanup. this.statistics = null; this.frontier = null; this.disk = null; this.scratchDisk = null; this.order = null; this.scope = null; if (this.settingsHandler != null) { this.settingsHandler.cleanup(); } this.settingsHandler = null; this.reserveMemory = null; this.processorChains = null; if (this.serverCache != null) { this.serverCache.cleanup(); this.serverCache = null; } if (this.checkpointer != null) { // 关闭Checkpointer,这里是关闭CheckPointer定时器 this.checkpointer.cleanup(); this.checkpointer = null; } if (this.bdbEnvironment != null) { // 关闭BDB数据库 try { this.bdbEnvironment.sync(); this.bdbEnvironment.close(); } catch (DatabaseException e) { e.printStackTrace(); } this.bdbEnvironment = null; } this.bigmaps = null; if (this.toePool != null) { this.toePool.cleanup();// 清理线程 } this.toePool = null; LOGGER.fine("Finished crawl."); }

   可以看到这段代码是一个清理的过程,同时触发这个方法的缘由是因为没有活动状态的线程.之前说过当Heritrix中的抓取线程ToeThread没有URL可抓取的时候就会自然死亡(不处于活动状态),当没有URL可抓取也就是意味着没有处于活动状态的线程,所以就会触发这个方法,导致抓取的结束。

 6.总结:

以上只是介绍了主要的方法和属性,其中还有一些重要的组件没有介绍,如Checkpoint,如果不去看源码可能根本不会注意到Checkpoint。不过以上的介绍大概说明了下Heritrix,你也可以通过这些方法调试一步步深入下去...

Heritrix源码分析(十三) Heritrix的控制中心(大脑)CrawlController(二)的更多相关文章

  1. Heritrix源码分析(九) Heritrix的二次抓取以及如何让Heritrix抓取你不想抓取的URL

    本博客属原创文章,欢迎转载!转载请务必注明出处:http://guoyunsky.iteye.com/blog/644396       本博客已迁移到本人独立博客: http://www.yun5u ...

  2. Heritrix源码分析(七) Heritrix总体介绍(转)

    本博客属原创文章,欢迎转载!转载请务必注明出处:http://guoyunsky.iteye.com/blog/642794         本博客已迁移到本人独立博客: http://www.yun ...

  3. Heritrix源码分析(十一) Heritrix中的URL--CandidateURI和CrawlURI以及如何增加自己的属性(转)

    本博客属原创文章,欢迎转载!转载请务必注明出处:http://guoyunsky.iteye.com/blog/649889 本博客已迁移到本人独立博客: http://www.yun5u.com/ ...

  4. Heritrix源码分析(十) Heritrix中的Http Status Code(Http状态码)(转)

    本博客属原创文章,欢迎转载!转载请务必注明出处:http://guoyunsky.iteye.com/blog/649737       本博客已迁移到本人独立博客: http://www.yun5u ...

  5. Heritrix源码分析(六) Heritrix的文件结构分析(转)

    本博客属原创文章,欢迎转载!转载请务必注明出处:http://guoyunsky.iteye.com/blog/642618      本博客已迁移到本人独立博客: http://www.yun5u. ...

  6. Heritrix源码分析(十四) 如何让Heritrix不间断的抓取(转)

    欢迎加入Heritrix群(QQ):109148319,10447185 , Lucene/Solr群(QQ) :  118972724 本博客已迁移到本人独立博客: http://www.yun5u ...

  7. Heritrix源码分析(十四)

    近段时间在搞定Lucene的一些问题,所以Heritrix源码分析暂时告一段落.今天下午在群里有同学提到了Heritrix异常终止的问题以及让Heritrix不停的抓取(就是抓完一遍后载入种子继续抓取 ...

  8. HDFS源码分析数据块复制监控线程ReplicationMonitor(二)

    HDFS源码分析数据块复制监控线程ReplicationMonitor(二)

  9. ABP源码分析十三:缓存Cache实现

    ABP中有两种cache的实现方式:MemroyCache 和 RedisCache. 如下图,两者都继承至ICache接口(准确说是CacheBase抽象类).ABP核心模块封装了MemroyCac ...

随机推荐

  1. 【转】dip,px,pt,sp 的区别

    dip: device independent pixels(设备独立像素). 不同设备有不同的显示效果,这个和设备硬件有关,一般我们为了支持WVGA.HVGA和QVGA 推荐使用这个,不依赖像素. ...

  2. CF 253B Two Heaps

    #include<stdio.h> #include<algorithm> #include<map> using namespace std; struct No ...

  3. AcmeAir安装AI探针--SaaS版

    一.安装SaaS版AI探针准备工作: 1.准备好可用的docker版AcmeAir应用 2.在SaaS官网tpm.oneapm.com上已注册可用的账号 3.登录tpm.oneapm.com,点击选择 ...

  4. Linux任务前后台的切换

    Shell支持作用控制,有以下命令实现前后台切换: 1. command& 让进程在后台运行 2. jobs 查看后台运行的进程 3. fg %n 让后台运行的进程n到前台来 4. bg %n ...

  5. Highgui.imwrite("/data/pic1111.png", mat)失败,且找不到报错

    ——> Highgui.imwrite("/data/pic1111.png", mat)失败,且找不到报错. ok -->直接使用以下代码,自己保存mat,从而可以跟 ...

  6. android eclipse——error: device not found解决办法

    device not found解决办法 http://www.blogjava.net/anchor110/articles/335866.html 问题描述:在CMD命令行,输入adb shell ...

  7. allegro使用汇总 [转贴]

    1.如何在allegro中取消花焊盘(十字焊盘) set up->design parameter ->shape->edit global dynamic shape parame ...

  8. pyhton与json,Xml

    对简单数据类型的encoding 和 decoding: 使用简单的json.dumps方法对简单数据类型进行编码,例如: 1 2 3 4 5 6 import json   obj = [[1,2, ...

  9. 下拉刷新控件(4)SwipeRefreshLayout官方教程(上)如何在应用中使用它

    http://developer.android.com/training/swipe/add-swipe-interface.html 1,在布局xml和代码中使用它 2,在menu中添加它 The ...

  10. pl/sql programming 06 异常处理

    如果 PLSQL发生了错误, 无论是系统错误还是应用错误, 都会抛出一个异常, 当前 PL/SQL 块中执行单元会暂停处理, 如果当前块有一个异常处理单元的话, 控制会转移到当前块的异常处理单元来处理 ...