关键技术之单机爬虫的实现(3)---URL存储到哪呢,存内存太占内存,存数据库效能不佳
这个问题,其实归根到底是空间与时间的问题。可以想象如果将url全部存在内存中,那么很快内存就会被全部占用。但是如果存在文件中,每次读取或者加入都去要操作文件。这个性能消耗是比较大的。因此,很快我们可以想到计算机中的缓存出现的原因不就是这点嘛。我的设计思路是:通过建立内存、文件、数据库三级存储。这样可以一定程度上取得满意的效果。 说明下我这里设计数据库主要是为分布式网络爬虫用的。就是当网络爬虫发现url时,我们把是本节点的任务的url存入文件。不是本节点任务的url存入公共数据库。这样就不需节点之间频繁的传递url,这是后面我们要说的。现在我们考虑单机爬虫,需要解决下面几个问题。
1、网络爬虫发现url时怎么存
2、网络爬虫需要取url时怎么取
3、存取不能破坏原先的顺利
这样的话,我们的设计三个队列。内存队列cache,文件队列finding,waitting队列。finding队列是负责存放从网页中分析出来的URL,该队列在磁盘上。waitting队列是用来存放每次从内存中写回到磁盘上的URL,该队列也在磁盘上,优先级高于finding队列。cache队列用来存放每次读到内存中将要进行DNS解析的URL,该队列在内存中。
首先,所有的URLs都被存在磁盘上的finding队列中,在common队列中,对于磁盘上的每个新的URL,都要对其hostname进行DNS解析,由于DNS解析的过程非常耗时,因此我们需要一个缓存去存放URLs,因此我们在内存中建了一个队列(cache队列),但是缓存的空间是有限的,当内存队列满了以后,这些多余URLs就会被写回到磁盘上,为了仍然保持原来的优先级,我们在磁盘上又建了一个队列(waitting队列)去存储那些被写回到磁盘上的URLs,否则如果仍被存于原来的finding队列中,根据队列的性质,将被写回到队列尾部,这样便会影响原有URL的优先级。因此,当内存中的队列下次有空余时,每次便先会从waitting队列中取URLs,当waitting队列空了以后,才会继续从finding队列中取得URL进行以后的工作。
我们建立一个UrlQueueManager类,下面是没有考虑waitting时候的参考代码:
public class UrlQueueManager
{
public Queue<string> cachequeue = new Queue<string>();
private FileIO finding= new FileIO();
public void Init()
{
finding.CloseWriteFile();
finding.CloseReadFile();
finding.OpenWriteFile(Spider.root + "finding.txt");
finding.OpenReadFile(Spider.root + "finding.txt");
}
public int Count
{
get
{
int count = 0;
count = cachequeue.Count;
return count;
}
}
public void Clear()
{
cachequeue.Clear();
}
public void Enqueue(UrlInfo url)
{
if (Count < Spider.urlqueuemaxcount)
{
cachequeue.Enqueue(url.ToString());
}
else
{
finding.WriteLine(url.ToString());
}
}
public UrlInfo Dequeue()
{
UrlInfo url = new UrlInfo();
if (Count > 0)
{
if (Count < Spider.urlqueuemaxcount / 2)
{
for (int i = 0; i < Spider.urlqueuemaxcount / 2; i++)
{
if (!finding.IsEof())
{
cachequeue.Enqueue(finding.ReadLine());
}
else
{
break;
}
}
}
string cacheurl = cachequeue.Dequeue();
string[] spiler = cacheurl.Split('|');
url.Url = spiler[0];
url.Depth = Convert.ToInt32(spiler[1]);
url.Weight = Convert.ToInt32(spiler[2]);
}
else
{
if (!finding.IsEof())
{
string waitingurl = finding.ReadLine();
string[] spiler = waitingurl.Split('|');
url.Url = spiler[0];
url.Depth = Convert.ToInt32(spiler[1]);
url.Weight = Convert.ToInt32(spiler[2]);
}
}
return url;
}
}
看明白了思路,实现问题不大。待续。。。