Go語言網路爬蟲的介面設計

2020-07-16 10:04:51
這裡所說的介面是指網路爬蟲框架中各個模組的介面。與先前描述的基本資料結構不同,它們的主要職責是定義模組的行為。在定義行為的過程中,我會對它們應有的功能作進一步的審視,同時也會更多地思考它們之間的共同作業方式。

下面就開始逐一設計網路爬蟲框架中的這類介面,以及相關的其他型別。為了更易於理解,先從那幾個處理模組的介面開始,然後再去考慮怎樣定義排程器以及它會用到的各種工具的行為。

下載器

下載器的功能就是從網路中的目標伺服器上下載內容。內容在網路中的唯一標識是網路地址,但是它只能起到定位的作用,並不是成功下載內容的充分條件。

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 的構成及生成方法。

組件 ID 的構成及生成方法
圖:元件 ID 的構成及生成方法