深入学习Heritrix---解析CrawlController
当我们以Web UI方式使用Heritrix时,点击任务开始(start)按钮时,Heritrix就开始了它的爬取工作.但它的内部
执行流程是怎样的呢?别急,下面将慢慢道来.
(一)CrawlJobHandler
当点击任务开始(start)按钮时,将执行它的startCrawler()方法:
if(sAction.equalsIgnoreCase("start"))
{
// Tell handler to start crawl job
handler.startCrawler();
}
再来看看startCrawler()方法的执行:
public class CrawlJobHandler implements CrawlStatusListener {
public void startCrawler() {
running = true;
if (pendingCrawlJobs.size() > 0 && isCrawling() == false) {
// Ok, can just start the next job
startNextJob();
}
}
protected final void startNextJob() {
synchronized (this) {
if(startingNextJob != null) {
try {
startingNextJob.join();
} catch (InterruptedException e) {
e.printStackTrace();
return;
}
}
startingNextJob = new Thread(new Runnable() {
public void run() {
startNextJobInternal();
}
}, "StartNextJob");
//当前任务线程开始执行
startingNextJob.start();
}
}
protected void startNextJobInternal() {
if (pendingCrawlJobs.size() == 0 || isCrawling()) {
// No job ready or already crawling.
return;
}
//从待处理的任务列表取出一个任务
this.currentJob = (CrawlJob)pendingCrawlJobs.first();
assert pendingCrawlJobs.contains(currentJob) :
"pendingCrawlJobs is in an illegal state";
//从待处理列表中删除
pendingCrawlJobs.remove(currentJob);
try {
this.currentJob.setupForCrawlStart();
// This is ugly but needed so I can clear the currentJob
// reference in the crawlEnding and update the list of completed
// jobs. Also, crawlEnded can startup next job.
this.currentJob.getController().addCrawlStatusListener(this);
// now, actually start
//控制器真正开始执行的地方
this.currentJob.getController().requestCrawlStart();
} catch (InitializationException e) {
loadJob(getStateJobFile(this.currentJob.getDirectory()));
this.currentJob = null;
startNextJobInternal(); // Load the next job if there is one.
}
}
}
由以上代码不难发现整个流程如下:
可以看出,最终将启动CrawlController的requestCrawlStart()方法.
(二)CrawlController
该类是一次抓取任务中的核心组件。它将决定整个抓取任务的开始和结束.
先看看它的源代码:
package org.archive.crawler.framework;
public class CrawlController implements Serializable, Reporter {
// 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;
}
CrawlOrder:它保存了对该次抓取任务中order.xml的属性配置。
CrawlScope:决定当前抓取范围的一个组件。
ProcessorChainList:从名称上可知,其表示处理器链。
Frontier:它是一个URL的处理器,决定下一个要被处理的URL是什么。
ToePool:它表示一个线程池,管理了所有该抓取任务所创建的子线程。
ServerCache:它表示一个缓冲池,保存了所有在当前任务中,抓取过的Host名称和Server名称。
在构造 CrawlController实例,需要先做以下工作:
(1)首先构造一个XMLSettingsHandler对象,将order.xml内的属性信息装入,并调用它的initialize方法进行初始化。
(2)调用CrawlController构造函数,构造一个CrawlController实例
(3)调用CrawlController的initilize(SettingsHandler)方法,初始化CrawlController实例。其中,传入的参数就是
在第一步里构造的XMLSettingsHandler实例。
(4 )当上述3步完成后,CrawlController就具备了运行的条件。此时,只需调用它的requestCrawlStart()方法,就
可以启动线程池和Frontier,然后开始不断的抓取网页。
先来看看initilize(SettingsHandler)方法:
public void initialize(SettingsHandler sH)
throws InitializationException {
sendCrawlStateChangeEvent(PREPARING, CrawlJob.STATUS_PREPARING);
this.singleThreadLock = new ReentrantLock();
this.settingsHandler = sH;
//从XMLSettingsHandler中取出Order
this.order = settingsHandler.getOrder();
this.order.setController(this);
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";
//检查了用户设定的UserAgent等信息,看是否符合格式
order.checkUserAgentAndFrom();
onFailMessage = "Unable to setup disk";
if (disk == null) {
setupDisk(); //设定了开始抓取后保存文件信息的目录结构
}
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();
}
onFailMessage = "Unable to setup bdb environment.";
//初始化使用Berkley DB的一些工具
setupBdb();
onFailMessage = "Unable to setup statistics";
setupStatTracking();
onFailMessage = "Unable to setup crawl modules";
//初始化了Scope、Frontier以及ProcessorChain
setupCrawlModules();
} catch (Exception e) {
String tmp = "On crawl: "
+ settingsHandler.getSettingsObject(null).getName() + " " +
onFailMessage;
LOGGER.log(Level.SEVERE, tmp, e);
throw new InitializationException(tmp, e);
}
Lookup.getDefaultCache(DClass.IN).setMaxEntries(1);
//dns.getRecords("localhost", Type.A, DClass.IN);
//实例化线程池
setupToePool();
setThresholds();
reserveMemory = new LinkedList<char[]>();
for(int i = 1; i < RESERVE_BLOCKS; i++) {
reserveMemory.add(new char[RESERVE_BLOCK_SIZE]);
}
}
可以看出在initilize()方法中主要做一些初始化工作,但这些对于Heritrix的运行是必需的.
再来看看CrawlController的核心,requestCrawlStart()方法:
public void requestCrawlStart() {
//初始化处理器链
runProcessorInitialTasks();
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方法,而Frontier将为线程池的线程提供URI,真正开始
抓取任务.至此,抓取任务开始.
主要参考:开发自己的搜索引擎—Lucene 2.0+Heritrix
转载于:https://www.cnblogs.com/hustcat/archive/2008/10/11/1308866.html
深入学习Heritrix---解析CrawlController相关推荐
- 分布式深度学习DDL解析
分布式深度学习DDL解析 一.概述 给一个庞大的GPU集群,在实际的应用中,现有的大数据调度器会导致长队列延迟和低的性能,该文章提出了Tiresias,即一个GPU集群的调度器,专门适应分布式深度学习 ...
- 深度学习常见问题解析
深度学习常见问题解析 计算机视觉与自动驾驶 今天 一.为什么深层神经网络难以训练? 1.梯度消失.梯度消失是指通过隐藏层从后向前看,梯度会变得越来越小,说明前面层的学习会显著慢于后面层的学习,所以学习 ...
- 【深度学习】解析神经网络中的数值稳定性、模型初始化和分布偏移(Pytorch)
[深度学习]解析神经网络中的数值稳定性.模型初始化和分布偏移 文章目录 1 概述1.1 梯度消失和梯度爆炸1.2 打破对称性 2 参数初始化 3 环境和分布偏移3.1 协变量偏移3.2 标签偏移3.3 ...
- 【深度学习】解析深度学习的集成方法
[深度学习]解析深度学习的集成方法 文章目录 1 前言 2 使用集成模型降低方差 3 如何集成神经网络模型3.1 Varying Training Data3.2 Varying Combinatio ...
- 用深度学习来解析梦境中出现的物体
这篇文章主要的工作算是机器学习和神经科学的结合工作,需要读者在这两个方向有一定的基础. 另有科普版本,结构更加清晰,不过删减了很多内容.科普版本如下: 用深度学习来解析梦境中出现的物体 - 行为与认知 ...
- HarmonyOS(一) 快速开始学习鸿蒙开发,官方文档学习路线解析
系列文章目录 HarmonyOS(一):快速开始学习鸿蒙开发,官方文档学习路线解析 HarmonyOS(二):应用开发环境搭建准备 HarmonyOS(三):创建你的第一个HelloWorld应用 文 ...
- 安卓学习之解析json数据
安卓学习之解析json数据 文章目录 安卓学习之解析json数据 前言 一.Json数据 二.使用原始的JsonObject来解析 1.请求数据 2.解析数据 2.1JsonObject解析数据 2. ...
- LOAM学习-代码解析(三)特征点运动估计 laserOdometry
LOAM学习-代码解析(三)特征点运动估计 laserOdometry 前言 一.初始化 二.去除位移畸变 TransformToStart TransformToEnd 三.去除角度畸变 Plugi ...
- 【深度学习】解析深度神经网络背后的数学原理
来源:产业智能官 解析深度网络背后的数学 如今,已有许多像 Keras, TensorFlow, PyTorch 这样高水平的专门的库和框架,我们就不用总担心矩阵的权重太多,或是对使用的激活函数求导时 ...
最新文章
- 【Python-ML】SKlearn库性能指标-混淆矩阵和F1
- 【Python】吐槽SQLAlchemy
- [蓝桥杯][2019年第十届真题]后缀表达式(正解!!)
- html5游戏制作入门系列教程(五)
- 修复jqgrid setgridparam postdata 的多次查询条件累加
- Burpsuit结合SQLMapAPI产生的批量注入插件
- android仿今日头条App、多种漂亮加载效果、选择器汇总、记事本App、Kotlin开发等源码...
- java的基本数据类型有什么特点_【Java】常用数据类型及其特点(万物都是变量)...
- [PKU 3580 3468][Noi 2005 Sequance]伸展树Splay 平衡树SBT(下)
- java8--Lambda表达式
- Windows 环境搭建 git 服务
- 在学习少儿编程中体会AI乐趣
- C++第一课(初识C++)
- MuMu模拟器连接AndroidStudio脚本
- 在QT框架下进行仿照PS图像调整功能设计(亮度对比度,色相饱和度及明度)
- AD导入PCB后模型出现绿色叉叉报错的解决办法
- 活灵活现用Git-基础篇
- 生命游戏和随机数之间某种不可言说的秘密
- 远景阿波罗光伏助力苹果供应商清洁能源计划
- Splunk机器学习应用(Machine Learning Toolkit)
热门文章
- 微服务网关从零搭建——(三)Ocelot网关 + identity4
- SAP MM 销售订单库存与普通库存之间相互转换过账后对于EBEWH以及MBEWH表的更新...
- flexpaper 背景色变化
- windows server 2003断开远程之后自动注销用户
- 开源用户界面和布局的套件XiaoCai.WinformUI(美化用户界面利器)
- CuteEditor 编辑器的字体样式无法控制的问题解决
- C51单片机中断定义
- Import error: no module named cv2 错误解决方法
- 解决使用mybatis分页插件PageHelper的一个报错问题
- A TLS packet with unexpected length was received 解决方法