这里所说的接口是指网络爬虫框架中各个模块的接口。与先前描述的基本数据结构不同,它们的主要职责是定义模块的行为。在定义行为的过程中,我会对它们应有的功能作进一步的审视,同时也会更多地思考它们之间的协作方式。
下面就开始逐一设计网络爬虫框架中的这类接口,以及相关的其他类型。为了更易于理解,先从那几个处理模块的接口开始,然后再去考虑怎样定义调度器以及它会用到的各种工具的行为。
下载器的功能就是从网络中的目标服务器上下载内容。内容在网络中的唯一标识是网络地址,但是它只能起到定位的作用,并不是成功下载内容的充分条件。
HTTP 协议是基于 TCP/IP 协议栈的应用层协议,它是互联网世界的根基之一。因此,互联网时代诞生的绝大多数语言都会使用不同的方式提供针对该协议的 API。当然,Go语言也不例外。Go 的标准库代码包 net/http 就提供了这类 API。
在编写网络爬虫框架的基本数据结构时,就用过其中的两个类型:http.Request 和 http.Response。实际上,我们将要构建的网络爬虫框架就是以 HTTP 协议和 net/http 代码包中的 API 为基础的。
从下载器充当的角色来讲,它的功能只有两个:发送请求和接收响应。因此,我可以设计出这样一个方法声明:
//用于根据请求获取内容并返回响应
Download(req *Request) (*Response, error)
Download 的签名完全体现出了下载器应有的功能。但是作为处理模块,下载器还应该拥有一些方法以供统计、描述之用。不过正因为这些方法是所有处理模块都应具备的,所以要编写一个更加抽象的接口类型。请看下面的声明:
//Module代表组件的基础接口类型。 //该接口的实现类型必须是并发安全的 type Module interface { //用于获取当前组件的ID ID() MID //用于获取当前组件的网络地址的字符串形式 Addr() string //用于荻取当前组件的评分 Score() uint64 //用于设置当前组件的评分 SetScore(score uint64) //用于获取评分计算器 ScoreCalculator() CalculateScore //用于获取当前组件被调用的计数 CalledCount() uint64 //用于获取当前组件接受的调用的计数, //组件一般会由于超负荷或参数有误而拒绝调用 AcceptedCount() uint64 //用于获取当前组件已成功完成的调用的计数 CompletedCount() uint64 //用于获取当前组件正在处理的调用的数量 HandlingNumber() uint64 //用于一次性获取所有计数 Counts() Counts //用于获取组件摘要 Summary() SummaryStruet }
处理模块之所以又称为组件,是因为它们实现的都是扩展功能,可组装到网络爬虫框架上。但同时它们又是重要的,因为如果没有它们,就无法使用这个框架编写出一个可以运转起来的网络爬虫。
Module 接口定义了组件的基本行为。其中,MID 是 string 的别名类型,它的值一般由 3 部分组成:标识组件类型的字母、代表生成顺序的序列号和用于定位组件的网络地址。网络地址是可选的,因为组件实例可以和网络爬虫的主程序处于同一个进程中。下面的模版声明可以很好地说明 MID 类型值的构成:
//组件ID的模板
var midTemplate = "%s%d|%s"
说到标识组件类型的字母,就要先介绍一下组件的类型。请看下面的声明:
//组件的类型 type Type string //当前认可的组件类型的常量 const ( //下载器 TYPE_DOWNLOADER Type = "downloader" //分析器 TYPE_ANALYZER Type = "analyzer" //棗目处理管道 TYPE_PIPELINE Type = "pipeline" )
组件类型常量的值已经直白地表达了其含义。基于此,我可以明确它们与字母之间的对应关系:
//合法的组件类型-字母的映射 var legalTypeLetterMap = map[Type]string{ TYPE_DOWNLOADER: "D", TYPE_ANALYZER: "A", TYPE_PIPELINE: "P", }
组件 ID 中的序列号可以由网络爬虫框架的使用方提供。这就需要我们在框架内提供一个工具,以便于统一序列号的生成和获取。序列号原则上是不能重复的,也是顺序给出的。但是如果序列号超出了给定范围,就可以循环使用。据此,我编写了一个序列号生成器的接口类型:
//序列号生成器的接口类型 type SNGenertor interface { //用于获取预设的最小序列号 Start() uint64 //用于获取预设的最大序列号 Max() uint64 //用于获取下一个序列号 Next() uint64 //用于获取循环计数 CycleCount() uint64 //用于获得一个序列号并准备下一个序列号 Get() uint64 }
其中最小序列号和最大序列号都可以由使用方在初始化序列号生成器时给定。循环计数代表了生成器生成的序列号在前两者指定的范围内循环的次数。
网络地址在 MID 中的格式是 "<IP>:<port>",例如 "127.0.0.1:8080",这类字符串其实就是 Module 接口的 Addr 方法返回的。
下图展示和总结了组件 ID 的构成及生成方法。