Nutch1.7源码再研究之---12 Fetch中的FetcherThread线程源码分析

本章主要讲解FetcherThread的代码。

这个线程的作用,源码上方有一句话:

 /**

 * This class picks items from queues and fetches the pages.

 */  

 所以这个线程充当消费者的角色。

---------------------------------------------------------------------------------------------------

刚开始自然是初始化一些变量,代码如下:

 

activeThreads.incrementAndGet(); // count threads

FetchItem fit = null;

这里的activeThreads的初始化见

private AtomicInteger activeThreads = new AtomicInteger(0);

也就是说先统计启动的线程个数。

同时置FetchItem fit为null.初始为空,因为还未取出来。

-----------------------------------------------------------------------

  接下来是一个典型的  

try {

catch (Throwable e) {

finally {

}

重点是try里面的部分,下面我们就来分析try里面的while循环的源码。

-------------------------------------------------------------------------------------------------------

首先是从队列里取出FetchItem.代码如下:

 

 

fit = fetchQueues.getFetchItem();

if (fit == null

{

if (feeder.isAlive() || fetchQueues.getTotalSize() > 0) {

LOG.debug(getName() + " spin-waiting ...");

// spin-wait.

spinWaiting.incrementAndGet();

try {

Thread.sleep(500);

catch (Exception e) {

}

spinWaiting.decrementAndGet();

continue;

else {

// all done, finish this thread

return;

}

}

  这段代码放到后面再分析...

------------------------------------------接下来是设置若干变量

lastRequestStart.set(System.currentTimeMillis());

Text reprUrlWritable = (Text) fit.datum.getMetaData().get(

Nutch.WRITABLE_REPR_URL_KEY);

if (reprUrlWritable == null

{

reprUrl = fit.url.toString();

else 

{

reprUrl = reprUrlWritable.toString();

}

 -------------------------------------------------------------------然后开始准备爬取这个网页了

先是重定向的准备

 

// fetch the page

redirecting = false;

redirectCount = 0;

---------------------------------------

接下来是打印日志:

 

do {

if (LOG.isInfoEnabled()) {

LOG.info("fetching "

+ fit.url

" (queue crawl delay="

fetchQueues

.getFetchItemQueue(fit.queueID).crawlDelay

"ms)");

}

if (LOG.isDebugEnabled()) {

LOG.debug("redirectCount=" + redirectCount);

LOG.info("redirectCount=" + redirectCount);

}

所以我们会看到

 

fetching http://www.baidu.com/ (queue crawl delay=5000ms)

redirectCount=0

--------------------------------接下来是获取爬虫协议及其机器人规则

 

redirecting = false;

Protocol protocol = this.protocolFactory

.getProtocol(fit.url.toString());

BaseRobotRules rules = protocol.getRobotRules(

fit.url, fit.datum);

一般情况下,我们的协议是HTTP协议,则其对应的分别是:

protocol--------------:org.apache.nutch.protocol.http.Http

BaseRobotRules--------------:crawlercommons.robots.SimpleRobotRules

这里面也是通过插件获取,所以如果你不想根据对方的robots协议的话,你可以把这段代码屏蔽掉来加速你的nutch爬虫。

------------------------------ 假如你屏蔽了robots协议,那么下面的代码对你来说是无用的。

 

if (!rules.isAllowed(fit.u.toString())) {

// unblock

fetchQueues.finishFetchItem(fit, true);

if (LOG.isDebugEnabled()) {

LOG.debug("Denied by robots.txt: "

+ fit.url);

}

output(fit.url, fit.datumnull,

ProtocolStatus.STATUS_ROBOTS_DENIED,

CrawlDatum.STATUS_FETCH_GONE);

reporter.incrCounter("FetcherStatus",

"robots_denied", 1);

continue;

}

if (rules.getCrawlDelay() > 0) {

if (rules.getCrawlDelay() > maxCrawlDelay

&& maxCrawlDelay >= 0) {

// unblock

fetchQueues.finishFetchItem(fit, true);

LOG.debug("Crawl-Delay for " + fit.url

" too long ("

rules.getCrawlDelay()

"), skipping");

output(fit.url,

fit.datum,

null,

ProtocolStatus.STATUS_ROBOTS_DENIED,

CrawlDatum.STATUS_FETCH_GONE);

reporter.incrCounter("FetcherStatus",

"robots_denied_maxcrawldelay", 1);

continue;

else {

FetchItemQueue fiq = fetchQueues

.getFetchItemQueue(fit.queueID);

fiq.crawlDelay = rules.getCrawlDelay();

if (LOG.isDebugEnabled()) {

LOG.info("Crawl delay for queue: "

+ fit.queueID + " is set to "

+ fiq.crawlDelay

" as per robots.txt. url: "

+ fit.url);

}

}

}

接下来就是正式爬取url对应的内容了。

---------------------------------------------

 

ProtocolOutput output = protocol.getProtocolOutput(

fit.url, fit.datum);

上面提到过protocol--------------:org.apache.nutch.protocol.http.Http

这里的getProtocolOutput实际上是HttpBase里的代码。

这里不具体讲如何获取http内容的。我之前的文章有讲如何添加连接池,

今日看到这里,发现上次连接池的添加方案不是最佳的,果然技术无涯。

---先看看ProtocolOutput的代码

public class ProtocolOutput {

  private Content content;

  private ProtocolStatus status;

  public ProtocolOutput(Content content, ProtocolStatus status) {

    this.content = content;

    this.status = status;

  }

  

  public ProtocolOutput(Content content) {

    this.content = content;

    this.status = ProtocolStatus.STATUS_SUCCESS;

  }

  

  public Content getContent() {

    return content;

  }

  public void setContent(Content content) {

    this.content = content;

  }

  public ProtocolStatus getStatus() {

    return status;

  }

  public void setStatus(ProtocolStatus status) {

    this.status = status;

  }

}

这个很简单,我就不分析了...

--------------------------------------- 接下来清除资源

 

ProtocolStatus status = output.getStatus();//获取爬取的状态

Content content = output.getContent();//得到内容

ParseStatus pstatus = null;

// unblock queue

fetchQueues.finishFetchItem(fit); //从队列中去掉这个item.

---------------------------------------------------

接下来是判断爬取结果的状态

 

switch (status.getCode()) {

 

case ProtocolStatus.SUCCESS// got a page

pstatus = output(fit.url, fit.datum, content,

status,

CrawlDatum.STATUS_FETCH_SUCCESS,

由于这个函数非常重要,所以下一篇文章中单独分析! 

你可能感兴趣的:(Nutch,Fetcher,FetcherThread)