1. 程式人生 > >02.Fabric原始碼解析---peer命令結構(王雅震)

02.Fabric原始碼解析---peer命令結構(王雅震)

Fabric原始碼解析2——peer命令結構

peer目錄結構

peer目錄結構自身十分清晰,一個main.go檔案,其餘資料夾除common,gossip外均為子命令集合,有chaincode,channel,clilogging,node,version五個,各司其職,供main.go整合使用。子命令資料夾中,與資料夾名稱相同的.go檔案為主要原始碼檔案,其餘的均為按功能劃分的動作命令原始碼。

以node目錄為例,node自身作為根命令的一個子命令,在node.go中實現,而node這個命令自身又有start,status,stop這三個動作去執行不同的任務,分別在對應start.go,status.go,stop.go中實現。注意,start,status,stop其實也是子命令,是node這個子命令的子命令,因為他們是在命令層級中最終去幹活的底層的人,我覺得用動作去形容他們更貼切一些。

chaincode
channel
clilogging
common
gossip
node 
node.go
start.go
status.go
stop.go
version
main.go

第三方包

在Getting Started中,無論是在啟動peer容器時預設執行的命令,還是手工執行交易時在終端視窗所輸入的命令,都類有類似的格式,如peer channel…,peer node…,peer chaincode…,這種 命令+子命令+選項 的風格,讓人在感覺上毫無違和感。peer命令主要依賴第三方包github.com/spf13/cobra,由其組成基本的peer命令架構。所以在此簡單介紹一下cobra。

cobra既是一個用於生成命令列程式的庫,也是用來生成程式和命令檔案的程式(即在命令列用cobra命令進行一系列操作,格式化生成一些使用cobra框架的原始碼檔案,使用者可在此基礎上進行程式設計)。目前,peer原始碼只將cobra當作一個庫進行使用。cobra基本用法如下:

建立一個(根)命令物件,其原型為Command,每個命令都是一個Command物件例項。建立命令物件其實就是填充Command中的成員的過程罷了。需要注意的是,Command中的成員還有很多,其中有一批欄位名為*Run,*RunE形式的成員,其作用與Run類似,區別在於執行的時間有先有後,是否被子命令繼承,是否返回錯誤。

type Command struct {  
    Use string   //命令名稱欄位,如你在命令列敲的是peer ...,則該欄位值就是"peer"
    Short string //短說明欄位
    Long string  //詳細說明欄位
    Run func(cmd *Command, args []string) //該命令所執行的函式
    ...
}

RootCmd := &cobra.Command{...}

如果有需要,對命令的新增flag,這一點可以簡單的理解為命令選項。

RootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")
RootCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from")

如果有需要,對根命令新增子命令,子命令與根命令本質是一樣的,只是人為的進行級別上的區分。

RootCmd.AddCommand(versionCmd)

執行命令

RootCmd.Execute()
由於文章重點在peer,所以在此只做簡單介紹,更詳細的使用方法,可在godoc或github.com/spf13/cobra上學習。其實閱讀fabric原始碼過程中有一個感覺,就是專案的大神們選用的第三方庫,一般都是既滿足需求,又比較容易學習和上手。

peer命令結構解析

我們現在正式從peer/main.go檔案開始解析原始碼,本文旨在解析peer的命令結構,因此只會涉及相關的原始碼,其他部分將會在其他主題文章中對應分析。如果你對cobra的用法稍微熟悉後,很容易就可以看懂main函式的構建。peer目錄下的子命令的原始碼結構非常類似,也基本逃不出上文介紹的關於cobra的基本操作。

首先定義了一個mainCmd命令,var mainCmd =&cobra.Command{…},該命令填充了Use,PersistentPreRunE和Run成員。Use如我們預見的那樣被賦值為peer,PersistentPreRunE先於Run執行,都被賦值了一個匿名函式。因為mainCmd只單純作為根命令,不實現由子命令實現的具體的交易事務,因此實現的只是PersistentPreRunE指定的檢查、初始化日誌系統並快取配置的功能,和Run指定的版本列印、命令幫助功能生成mainCmd物件的命令列標識物件mainFlags,mainFlags := mainCmd.PersistentFlags(),也就是peer命令的選項,並對該標識對像進行了維護,增加了version,logging_level兩個選項。這也對應了其在自身物件中設定PersistentPreRunE和Run的功能。

首先定義了一個mainCmd命令,var mainCmd =,mainCmd.AddCommand(…)。新增的命令有

1. version.Cmd()
2. node.Cmd()
3. chaincode.Cmd(nil)
4. clilogging.Cmd(nil)
5. channel.Cmd(nil)

Cmd()是每個子命令檔案中暴露出的函式,各自整合了各自的動作命令。

**啟動根命令,mainCmd.Execute()。**啟動了根命令,也就啟動了其下的所有命令。子命令結構解析,以node為例其實讀懂了peer命令,其餘的子命令類推即可。在此還是囉嗦兩句吧。上文已經說了子命令的原始碼結構是極其相似的,這裡只以node為例。

**在node.go中,**首先定義了一個node命令物件,var nodeCmd = &cobra.Command{…}

**在Cmd函式中,**添加了startCmd(),statusCmd(),stopCmd()三個函式返回的start,status,stop子命令(動作命令),分別實現在start.go,status.go,stop.go。這三個命令的原始碼結構也是基本一致,在此僅以start和start.go為例。

在start.go中,首先定義了一個start命令物件,var nodeStartCmd = &cobra.Command{…},其中對RunE成員賦值了一個匿名函式,函式體中執行了serve函式,這也是該命令最終會呼叫的函式。serve函式是一個非常重要,非常複雜的函式。記不記得在上篇介紹Fabric專案線頭的文章提到過的,在每個peer容器啟動後預設執行的就是peer node start –peer-defaultchain=false命令,在此處就對接上了,該命令最終呼叫執行的就是serve函式,同時也就是說,serve函式會做了很多很多的準備工作。