• Go语言网络爬虫内部基础接口

    首先要做的是,先为组件通用功能定义一个内部接口,这里把它叫做组件的内部基础接口。内部基础接口及其实现类型存放在了代码包 gopcp.v2/chapter6/webcrawler/module/stub 中,代码包可以在我的网盘中下载(链接:https://pan.baidu.com/s/1yzWHnK1t2jLDIcTPFMLPCA 提取码:slm5)。

    该接口内嵌了之前讲过的 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 接口的方法就相当简单了,唯一要注意的就是充分利用原子操作保证它们的并发安全。这里就不展示了。或许大家可以试着编写出来,然后对比看看。

更多...

加载中...