作者:笑侃
1. 蜘蛛程序长啥样?
蜘蛛程序的是搜索引擎中最关键的后台程序之一,它必须十分可靠,可以长期运行而无需经常维护。但是我往往看到许多朋友开始做蜘蛛程序的时候总是将很多精力放到了界面上。个人觉得这是没有必要的,我们应该注重这个程序的可靠性和效率。
(图1. Coolgobot,笑侃的第一个蜘蛛程序)
(图2. 有简单界面的蜘蛛程序)
(图3. Webus系统中最有效的蜘蛛程序之一)
2. 蜘蛛程序的结构(图4. 蜘蛛程序结构)
2.1 用队列来传递Url数据
在图4中,Url Reader将Url数据读取到一个队列中,再由下载线程去队列中取Url并且下载。使用队列的好处是可以对于流程容易控制,而且方便实现多线程方式。
2.2 两种多线程方案
对于蜘蛛程序而言有两种多线程方案
a. 抢先式多线程
这种方法简单来说就是一开始只有一个线程在工作,当这个线程下载了一个页面之后,Parser马上从页面中提取新的Url List,然后针对每个Url,程序都会启动一个新的线程去下载,直到达到最大线程数为止。
b. 循环式多线程
这种方法就是一开始就开启N个线程,每个线程都会自己去Queue中取Url进行下载工作,完成一个,就取下一个。直到没有新的Url了,线程就进入休眠(Sleep)状态。
这两种方案中,我比较倾向于b方案,因为采用这种方案将使得程序的结构更加清晰,功能模块划分也更加合理。所以后面我所说的多线程都是指循环式的多线程方案。
2.3 将临时页面数据保存为文件
我看到很多朋友开始写蜘蛛程序时,为了追求高效率,将下载的页面数据都放到一个Queue中,等待分析线程取逐一分析。这种方式固然可以让整个程序跑起来,但是能够跑多长时间就难说了。因为此时整个系统的稳定性和Queue的大小成反比。Queue越大,占用内存就越多,系统稳定性就越差。可能运行一两天看起来还不错,要是时间一长,程序就变得不那么健康了,最后就会崩溃。
我们不妨将临时页面保存为文件,然后用一个Queue将要处理得文件名传递给分析线程,再由分析线程去处理对应的文件。这种处理方式就比上面的一种好得多,系统的可靠性又大大加强了。
2.4 分析线程 or 分析程序?
页面下载下来,需要分析哪些数据和具体的需求相关,我们这里就不讨论这个问题了。这里说说“分析线程”还是“分析程序”的问题。
其实将分析部分作为线程还是单独的程序来实现在逻辑上是没有什么区别的。这两种方法应该可是实现同样的可靠性和效率。但是作为程序会比作为线程实现起来麻烦一些。
作为线程,下载线程和分析线程之间的数据传递通过一个Queue就行了。如果作为程序,就需要考虑到两个进程之间的通讯,这样会比较麻烦一些。
2.5 Url消重
为了避免蜘蛛程序重复下载同一个页面,我们需要对Url消重。这里有两种方案可以实现Url消重。一种利用数据库,针对Url字段建立唯一约束……。这是一种偷懒的方法,当数据量不大时还是很有效的,主要是无需编程,实现简单。另外一种就是利用信息指纹的方法来实现。关于这部分,可以参看:http://googlechinablog.com/2006/08/blog-post.html
3. 我们要思考的问题
3.1 头号问题:OutOfMemoryException
如果没有搞错的话,内存溢出是蜘蛛程序会遇到的头号问题。它的特点是:不定性、突发性、灾难性。要解决这个问题,我们首先要改变自己的编程习惯。如果想要自己的蜘蛛长命百岁,就要采取节制的内存策略,呵呵,内存就像脂肪,太多了终究是会导致疾病的。那为什么这个问题会体现出不定性、突发性呢?这和WIN32的内存机制有关,关于这部分,可以参看:http://my.opera.com/talkinsmile/blog/show.dml/407303
3.2 异常管理
微软的大师说过:如果没有好的异常管理策略,就干脆不要尝试去管理。但他并不是要我们对异常采取听之任之的态度。至少我们应该捕获可能发生的异常。如果你希望自己的蜘蛛任劳任怨,不会罢工,就好好检查一下和以下名词相关的程序代码中是否已经做了完备的异常处理:
网络
数据库
线程
磁盘IO
超出值范围
数组下标溢出
还有什么?太多了,大家自己去发现吧^_^
3.3 分布式
对于开发蜘蛛程序的人而言,首要关心的是“多线程”然后就是“分布式”了。分布式是一个比较复杂的问题,需要分析具体的部署条件和业务需求,从而选择一种合适的设计模式来实现分布式应用。
针对蜘蛛程序,我个人最喜欢的是管道式的分布式设计模式。所谓管道就是一个:Url -> 下载 -> 分析 -> 提交数据的完整过程,管道式分布式设计模式的思想就是通过一个管理器将很多这种管道都并起来一同工作,如果哪条管道出现问题了,也不会影响其它的管道正常工作。
关于这部分,大家可以自己到找找,很多的。
[此贴子已经被作者于2007-8-6 17:59:16编辑过]