Go语言网络爬虫内部基础接口
首先要做的是,先为组件通用功能定义一个内部接口,这里把它叫做组件的内部基础接口。内部基础接口及其实现类型存放在了代码包 gopcp.v2/chapter6/webcrawler/module/stub 中,代码包可以在我的网盘中下载(链接:https://pan.baidu.com/s/1yzWHnK1t2jLDIcTPFMLPCA 提取码:slm5)。
该接口内嵌了之前讲过的 Module 接口,其声明如下:
由于通常情况下外部不应该宜接改变组件的内部状态,所以该接口的名字才以 "Internal" 为后缀,以起到提示的作用。并且,在 gopcp.v2/chapter6/webcrawler/module 包中公开的程序实体并没有涉及该接口。
ModuleInternal 接口及其实现类型只是为了方便自行编写组件的人而准备的。在编写组件时也用到了它们。
ModuleInternal 接口是 Module 接口的扩展,前者的实现类型自然也是后者的实现类型。这个实现类型命名为 myModule,它的基本结构如下:
如果组件地址为空,就说明该组件与网络爬虫程序同处在一个进程中。这时的 addr 字段自然就是 ""。module 包的 SplitMID 函数用于分离出组件 ID 的各个部分,并在组件 ID 不符合规范时报错,它是 module 包中众多工具类函数中的一个。
与之相对应,module 包中还有一个 GenMID 函数,用它可以生成组件 ID。调用 GenMID 函数时,需要给定一个序列号。大家可以通过调用 module 包中的 NewSNGenertor 函数创建出一个序列号生成器。强烈建议把序列号生成器的实例赋给一个全局变量。
组件评分计算器理应由外部提供,并且一般会为同一类组件实例设置同一种组件评分计算器,而且一旦设置就不允许更改。所以,即使是 ModuleInternal 接口也没有提供改变它的方法。
再强调一下,NewIllegalParameterError 是 gopcp.v2/chapter6/webcrawler/errors 包中的函数。该包中还有 NewCrawlerError 和 NewCrawlerErrorBy 函数,用于生成爬虫程序运作过程中抛出的错误值。
有了上述的那些字段,实现 ModuleInternal 接口的方法就相当简单了,唯一要注意的就是充分利用原子操作保证它们的并发安全。这里就不展示了。或许大家可以试着编写出来,然后对比看看。
该接口内嵌了之前讲过的 Module 接口,其声明如下:
//组件的内部基础接口的类型 type ModuleInternal interface { module.Module //把调用计数增 1 IncrCalledCount() //把接受计数增1 IncrAcceptedCount() //把成功完成计数增 1 IncrCompletedCount() //把实时处理数增 1 IncrHandlingNumber() //把实时处理数减 1 DecrHandlingNumber() //用于清空所有计数 Clear() }Module 接口中声明的更多的是获取内部状态的方法,比如:获取组件 ID、组件地址、各种计数值,等等。而在 ModuleInternal 接口中,添加的方法都是改变内部状态的方法。
由于通常情况下外部不应该宜接改变组件的内部状态,所以该接口的名字才以 "Internal" 为后缀,以起到提示的作用。并且,在 gopcp.v2/chapter6/webcrawler/module 包中公开的程序实体并没有涉及该接口。
ModuleInternal 接口及其实现类型只是为了方便自行编写组件的人而准备的。在编写组件时也用到了它们。
ModuleInternal 接口是 Module 接口的扩展,前者的实现类型自然也是后者的实现类型。这个实现类型命名为 myModule,它的基本结构如下:
//组件内部基曲接口的实现类型 type myModule struct { //组件ID mid module.MID //组件的网络地址 addr string //组件评分 score uint64 //评分计算器 scoreCalculator module.CalculateScore //调用计数 calledCount uint64 //接受计数 acceptedCount uint64 //成功完成计数 completedCount uint64 //实时处理数 handlingNumber uint64 }这些字段都是理所应当存在的,它们分别与 Module 接口(以及 ModuleInternal 口)中声明的方法有直接的对应关系。按照惯例, NewModuleInternal 用于新建一个 ModuleInternal 类型的实例,它的声明如下。
//创建一个组件内部基础类型的实例 func NewModuleInternal( mid module.MID, scoreCalculator module.CalculateScore) (ModuleInternal, error) { parts, err := module.SplitMID(mid) if err != nil { return nil, errors.NewIllegalParameterError( fmt.Sprintf("illegal ID %q: %s", mid, err)) } return &myModule{ mid: mid, addr: parts[2], scoreCalculator: scoreCalculator }, nil }myModule 类型中的字段有几个是需要显式初始化的,包括:组件 ID、组件的网络地址(下面简称组件地址)和组件评分计算器。参数 mid 提供了组件 ID,同时也提供了组件地址。因为组件 ID 中可以包含组件地址。
如果组件地址为空,就说明该组件与网络爬虫程序同处在一个进程中。这时的 addr 字段自然就是 ""。module 包的 SplitMID 函数用于分离出组件 ID 的各个部分,并在组件 ID 不符合规范时报错,它是 module 包中众多工具类函数中的一个。
与之相对应,module 包中还有一个 GenMID 函数,用它可以生成组件 ID。调用 GenMID 函数时,需要给定一个序列号。大家可以通过调用 module 包中的 NewSNGenertor 函数创建出一个序列号生成器。强烈建议把序列号生成器的实例赋给一个全局变量。
组件评分计算器理应由外部提供,并且一般会为同一类组件实例设置同一种组件评分计算器,而且一旦设置就不允许更改。所以,即使是 ModuleInternal 接口也没有提供改变它的方法。
再强调一下,NewIllegalParameterError 是 gopcp.v2/chapter6/webcrawler/errors 包中的函数。该包中还有 NewCrawlerError 和 NewCrawlerErrorBy 函数,用于生成爬虫程序运作过程中抛出的错误值。
有了上述的那些字段,实现 ModuleInternal 接口的方法就相当简单了,唯一要注意的就是充分利用原子操作保证它们的并发安全。这里就不展示了。或许大家可以试着编写出来,然后对比看看。
所有教程
- C语言入门
- C语言编译器
- C语言项目案例
- 数据结构
- C++
- STL
- C++11
- socket
- GCC
- GDB
- Makefile
- OpenCV
- Qt教程
- Unity 3D
- UE4
- 游戏引擎
- Python
- Python并发编程
- TensorFlow
- Django
- NumPy
- Linux
- Shell
- Java教程
- 设计模式
- Java Swing
- Servlet
- JSP教程
- Struts2
- Maven
- Spring
- Spring MVC
- Spring Boot
- Spring Cloud
- Hibernate
- Mybatis
- MySQL教程
- MySQL函数
- NoSQL
- Redis
- MongoDB
- HBase
- Go语言
- C#
- MATLAB
- JavaScript
- Bootstrap
- HTML
- CSS教程
- PHP
- 汇编语言
- TCP/IP
- vi命令
- Android教程
- 区块链
- Docker
- 大数据
- 云计算