9.3 总体设计

参看在上一节中展示的数据流程图可知,网络爬虫框架的处理模块有3个:网页下载器、分析器和条目处理管道。加之协调和调度这些处理模块的运行的控制模块,我们就可以明晰该框架的模块划分了。我们把这里提到的控制模块称为调度器。下面是这4个模块的各自承担的职责。

  • 网页下载器:接受作为输入的请求,并将该请求转换成HTTP请求;将该HTTP请求发送至与其中的网络地址对应的远程服务器;在HTTP请求发送完成之后,立即等待相应的HTTP响应的到来;在收到HTTP响应之后,将其封装成响应并作为输出返回给网页下载器的调用方。其中,被用于发送HTTP请求和接收HTTP响应的HTTP客户端程序的生成方式,应该可以由网络爬虫框架的使用方自行定义。另外,若在该子流程被执行期间发生了错误,应该立即以适当的方式告知网络爬虫框架的使用方。对于其他模块来讲,也应该是这样。

  • 分析器:接受作为输入的响应,并将该响应还原成HTTP响应;对该HTTP响应的状态和内容进行检查,并根据给定的规则进行筛选以及生成新的请求或条目;将生成的请求或条目作为输出返回给分析器的调用方。在分析器的职责中,我们可以想到的能够留给网络爬虫框架的使用方自定义的部分并不少。例如,内容筛选的规则、生成请求和条目的方式,甚至是可以接受的HTTP响应状态的定义。在后面,我们会对这些可能自定义的部分进行一些取舍。

  • 条目处理管道:接受作为输入的条目,并对其执行一系列的处理步骤;条目处理管道中可以产出最终的数据;这个最终的数据可以在其中的某个处理步骤中被持久化(不论是本地存储还是发送给远程的存储服务器)以备后用。我们可以把上面所说的一系列处理步骤留给网络爬虫框架的使用方自行定义。这样,网络爬虫框架就可以真正地与条目处理细节脱离开来了。网络爬虫框架应该丝毫不关心这些条目会怎样被处理和持久化。它仅仅负责控制整体的处理流程。我们把负责单个处理步骤的程序称为条目处理器。条目处理器接受条目,并把被处理完成的条目返回给条目处理管道。条目处理管道会紧接着把该条目传递给下一个条目处理器,直至给定的条目处理器序列中的每个条目处理器都依次处理过该条目为止。

  • 调度器:调度器仅接受作为输入的首次请求,并且不会产生任何输出。调度器的主要职责是调度各个处理模块的运行。其中包括维护各个处理模块的实例、在不同的处理模块实例之间传递数据(请求、响应或条目),以及监控所有这些被调度者的状态,等等。有了调度器的支持,各个处理模块得以保持其职责的简洁和专一。而由于调度器是网络爬虫框架中最重要的一个模块,所以我们可能需要再编写出一些工具来支撑起它的功能。其中很多工具的实现是由我们在前几章中实现的代码演变而来的。读者在阅读后面的相应内容时会有所体会。

在弄清楚网络爬虫框架中的各个模块的职责之后,我们会发现该框架是以调度器为核心的。此外,为了并发执行的需要,除调度器之外的其他模块都可以是多实例的。它们应该由调度器创建、维护和调用。反过来讲,这些处理模块的实例会从调度器处接受输入,并在进行相应的处理之后将输出返回给调度器。最后,与另外两个处理模块相比,条目处理管道是比较特殊的。顾名思义,它应该是以流式处理为基础的。它的设计灵感来自于我们之前讲到过的Linux操作系统所提供的管道。我们可以不断地向该管道发送条目,而该管道则会让其中的若干个条目处理器依次处理每一个条目。我们可以很轻易地使用一些同步方法来保证条目处理管道的并发安全性,因此调度器可以只持有该管道的一个实例。不过,我们是否可以并发地向该管道发送条目还要根据那些条目处理器的实现方式来抉择。还记得吗?这些条目处理器是由网络爬虫框架的使用方提供的。

图9-2展示了调度器与各个处理模块的关系。我在图中加入了一个新的元素——中间件。实际上,我们之前所说的被用来支撑调度器的管理功能的那些工具就是中间件的一部分。更明确地讲,中间件不是一个完整的模块,而是一个工具集。调度器会通过特定的工具来实现对各个处理模块的控制,也会使用一些工具在多个处理模块之间传递各种类型的数据。由此,这些工具在网络爬虫框架中的作用可见一斑。它们是调度器与所有处理模块之间的桥梁。

{%}

图 9-2 调度器与各个处理模块的关系

至此,读者应该对网络爬虫框架的总体设计有了一个宏观上的认识。不过,我们还未提及在这个总体设计之下包含的大量的设计技巧和决策。这些技巧和决策不但与一些通用的程序设计原则有关,还涉及了很多依赖于Go语言自身的编程风格和方式。这也从侧面说明,由于几乎所有编程语言都有着非常鲜明的特点和比较擅长的领域,因此我们在设计一个需要由特定的编程语言实现的软件或程序的时候,多多少少会考虑到这门编程语言自身的特性。也就是说,软件设计不是与具体的编程语言毫不相关的。反过来讲,总会有一门或少数几门编程语言非常适合实现某一类的软件或程序。

我们在后面会看到,就本章所讲的网络爬虫框架而言,Go语言是非常适合作为其实现语言的。不过,在动手实现这个框架之前,我们会先讲一讲其中每一个部分(各个模块以及中间件)的设计方案。