爬虫Larbin解析(二)——sequencer()
分析的函数: void sequencer()
//位置:larbin-2.6.3/src/fetch/sequencer.cc
void sequencer() { bool testPriority = true; if (space == 0) //unit space = 0 { space = global::inter->putAll(); } int still = space; if (still > maxPerCall) //#define maxPerCall 100 still = maxPerCall; while (still) { if (canGetUrl(&testPriority)) { --space; --still; } else { still = 0; } } }
所在的文件
larbin-2.6.3/src/fetch/sequencer.h、larbin-2.6.3/src/fetch/sequencer.cc
一、 对于space = global::inter->putAll();
1. interf在global.cc(位置:/larbin-2.6.3/src/global.cc)中的定义为
inter = new Interval(ramUrls); //#define ramUrls 100000 (位置:larbin-2.6.3/src/types.h)
批注:区别 inter = new Interval(ramUrls); 和 inter = new Interval[ramUrls]; 前一个()内是参数,要传入构造函数的;后一个[]内是开辟数组的个数。
2. 类 Interval定义(位置:/larbin-2.6.3/src/fetch/site.h)
批注:类内的函数定义为inline。对内联函数的几点说明:
- 内联函数避免函数调用的开销。将函数指定为内联函数,(通常)就是将它在程序的每个调用点上“内联地”展开,消除调用函数进行的额外开销(调用前先保存寄存器,并在返回时回复)。内联说明(在函数返回值前加inline)对编译器来说只是一个建议,编译器可以选择忽略。一般内敛函数适用于优化小的、只有几行、经常被调用的函数。大多数编译器不支持递归函数的内敛。
- 把内联函数放在头文件。以便编译器能够在调用点展开同一个函数(保证编译器可见、所有的定义相同)。
- 编译器隐式地将在类内定义的成员函数当作为内联函数.
二、 对于canGetUrl(&testPriority)
函数定义(位置larbin-2.6.3/src/fetch/sequencer.cc)
/* Get the next url * here is defined how priorities are handled 按优先级从各个URL队列 (比如URLsDisk,URLsDiskWait或URLsPriority,URLsPriorityWait) 获取url保存到某个NameSite(通过url的hash值) at "global.cc" // FIFOs URLsDisk = new PersistentFifo(reload, fifoFile); URLsDiskWait = new PersistentFifo(reload, fifoFileWait); URLsPriority = new SyncFifo<url>; URLsPriorityWait = new SyncFifo<url>; */ static bool canGetUrl (bool *testPriority) { url *u; if (global::readPriorityWait != 0) // 在global.cc声明定义: uint global::readPriorityWait=0; { global::readPriorityWait--; u = global::URLsPriorityWait->get(); global::namedSiteList[u->hostHashCode()].putPriorityUrlWait(u); return true; } else if (*testPriority && (u=global::URLsPriority->tryGet()) != NULL) { // We've got one url (priority) global::namedSiteList[u->hostHashCode()].putPriorityUrl(u); return true; } else { *testPriority = false; // Try to get an ordinary url if (global::readWait) { global::readWait--; u = global::URLsDiskWait->get(); global::namedSiteList[u->hostHashCode()].putUrlWait(u); return true; } else { u = global::URLsDisk->tryGet(); if (u != NULL) { global::namedSiteList[u->hostHashCode()].putUrl(u); return true; } else { return false; } } } }
1. 为什么disk和priority的队列都是成对出现的,是因为可以认为每个site在namedSiteList当中都有一个小的队列来保存它的url,这个url的个数是有个数限制的,当超过这个限制的时候就不能再把该site下的url放入,但也不能丢弃,而是放入wait队列。Larbin会控制一段时间在disk队列中取url,一段时间在diskWait当中取url。disk和priority的区别只是优先级的区别。namedSiteList的作用是实现了DNS缓存。
2. global::readPriorityWait 的值由main.cc的cron()函数中变化得知
// see if we should read again urls in fifowait if ((global::now % 300) == 0) { global::readPriorityWait = global::URLsPriorityWait->getLength(); global::readWait = global::URLsDiskWait->getLength(); } if ((global::now % 300) == 150) { global::readPriorityWait = 0; global::readWait = 0; }
这里global::now%300是判断这次是对wait里的url进行处理,还是对不是wait里的进行处理,这里的%300等于0和150的概率都是1/300,所以大约300次换一次。readPriorityWait是URLsPriorityWait中的长度(也就是url的数量);readWait是URLsDiskWait中url的个数。
3. 在canGetUrl中,在对于每个站点,将相应的url放进去。putPriorityUrlWait, putPriorityUrl, putUrlWait, putUrl在site.h的定义如下
/** Put an url in the fifo * If there are too much, put it back in UrlsInternal * Never fill totally the fifo => call at least with 1 */ void putGenericUrl(url *u, int limit, bool prio); inline void putUrl(url *u) { putGenericUrl(u, 15, false); } inline void putUrlWait(url *u) { putGenericUrl(u, 10, false); } inline void putPriorityUrl(url *u) { putGenericUrl(u, 5, true); } inline void putPriorityUrlWait(url *u) { putGenericUrl(u, 1, true); }
可以发现,每次都是调用函数putGenericUrl,其定义如下
/* Put an url in the fifo if their are not too many */ void NamedSite::putGenericUrl(url *u, int limit, bool prio)
{ if (nburls > maxUrlsBySite - limit)
{ // Already enough Urls in memory for this Site // first check if it can already be forgotten if (!strcmp(name, u->getHost()))
{ if (dnsState == errorDns)
{ nburls++; forgetUrl(u, noDNS); return; } if (dnsState == noConnDns)
{ nburls++; forgetUrl(u, noConnection); return; } if (u->getPort() == port && dnsState == doneDns && !testRobots(u->getFile()))
{ nburls++; forgetUrl(u, forbiddenRobots); return; } } // else put it back in URLsDisk refUrl(); global::inter->getOne(); if (prio)
{ global::URLsPriorityWait->put(u); }
else
{ global::URLsDiskWait->put(u); } }
如果已经有足够多的url在内存里,执行这里if中的代码,strcmp(name,u->getHost())是判断这个主机是不是已经就进行过dns方面的判断,也就是说对于一个站点,只做一次dns解析的判断,以后就按这个结果进行处理,dnsState有noDns,noConnDns,还有robots.txt不允许的情况,如果没有问题,就把它放到URLsDisk中。
else { nburls++; if (dnsState == waitDns || strcmp(name, u->getHost()) || port != u->getPort() || global::now > dnsTimeout) { // dns not done or other site putInFifo(u); addNamedUrl(); // Put Site in fifo if not yet in if (!isInFifo) { isInFifo = true; global::dnsSites->put(this); } } else switch (dnsState) { case doneDns: transfer(u); break; case errorDns: forgetUrl(u, noDNS); break; default: // noConnDns forgetUrl(u, noConnection); } }
如果需要判断dns能不能解析,就将它放到dnsSites里,这个会在fetchDns中判断。或是如果还能放到内存里,并且又是doneDns,表示可以解析,就调用transfer:
void NamedSite::transfer(url *u) { if (testRobots(u->getFile())) { if (global::proxyAddr == NULL) { memcpy(&u->addr, &addr, sizeof(struct in_addr)); } global::IPSiteList[ipHash].putUrl(u); } else { forgetUrl(u, forbiddenRobots); } }
这里是将url放入到IPSiteList的相应ipHash中。
附类的定义
类url定义(larbin-2.6.3/src/utils/url.h larbin-2.6.3/src/utils/url.cc)
global::namedSiteList
NamedSite *global::namedSiteList; namedSiteList = new NamedSite[namedSiteListSize];
其中两个类的定义
larbin-2.6.3/src/utils/PersistentFifo.h、larbin-2.6.3/src/utils/PersistentFifo.cc
Larbin-2.6.3/src/utils/syncFifo.h
本文转自jihite博客园博客,原文链接:http://www.cnblogs.com/kaituorensheng/p/3789621.html,如需转载请自行联系原作者