首先要做的是,先為元件通用功能定義一個內部介面,這裡把它叫做元件的內部基礎介面。內部基礎介面及其實現型別存放在了程式碼包 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 介面的方法就相當簡單了,唯一要注意的就是充分利用原子操作保證它們的並行安全。這裡就不展示了。或許大家可以試著編寫出來,然後對比看看。