Fabric 1.0原始碼分析(25) Orderer

尹成發表於2018-05-20
# Fabric 1.0原始碼筆記 之 Orderer

## 1、Orderer概述

Orderer,為排序節點,對所有發往網路中的交易進行排序,將排序後的交易安排配置中的約定整理為塊,之後提交給Committer進行處理。

Orderer程式碼分佈在orderer目錄,目錄結構如下:

* orderer目錄
    * main.go,main入口。
    * util.go,orderer工具函式。
    * metadata目錄,metadata.go實現獲取版本資訊。
    * localconfig目錄,config.go,本地配置相關實現。
    * ledger目錄,賬本區塊儲存。
        * file目錄,賬本區塊檔案儲存。
        * json目錄,賬本區塊json檔案儲存。
        * ram目錄,賬本區塊記憶體儲存。
    * common目錄,通用程式碼。
        * bootstrap目錄,初始區塊的提供方式。
# Fabric 1.0原始碼筆記 之 Orderer #orderer start命令實現



## 1、載入命令列工具並解析命令列引數

orderer的命令列工具,基於gopkg.in/alecthomas/kingpin.v2實現,地址:http://gopkg.in/alecthomas/kingpin.v2。
相關程式碼如下

```go
var (
    //建立kingpin.Application
    app = kingpin.New("orderer", "Hyperledger Fabric orderer node")
    //建立子命令start和version
    start = app.Command("start", "Start the orderer node").Default()
    version = app.Command("version", "Show version information")
)

kingpin.Version("0.0.1")
//解析命令列引數
switch kingpin.MustParse(app.Parse(os.Args[1:])) {
case start.FullCommand():
    //orderer start的命令實現,下文展開講解
case version.FullCommand():
    //輸出版本資訊
    fmt.Println(metadata.GetVersionInfo())
}
//程式碼在orderer/main.go
```

metadata.GetVersionInfo()程式碼如下:

```go
func GetVersionInfo() string {
    Version = common.Version //var Version string,全域性變數
    if Version == "" {
        Version = "development build"
    }

    return fmt.Sprintf("%s:\n Version: %s\n Go version: %s\n OS/Arch: %s",
        ProgramName, Version, runtime.Version(),
        fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH))
}
//程式碼在orderer/metadata/metadata.go
```

## 2、載入配置檔案

配置檔案的載入,基於viper實現,即https://github.com/spf13/viper。

```go
conf := config.Load()
//程式碼在orderer/main.go
```

conf := config.Load()程式碼如下:

```go
func Load() *TopLevel {
    config := viper.New()
    //cf.InitViper作用為載入配置檔案路徑及設定配置檔名稱
    cf.InitViper(config, configName) //configName = strings.ToLower(Prefix),其中Prefix = "ORDERER"

    config.SetEnvPrefix(Prefix) //Prefix = "ORDERER"
    config.AutomaticEnv()
    replacer := strings.NewReplacer(".", "_")
    config.SetEnvKeyReplacer(replacer)

    err := config.ReadInConfig() //載入配置檔案內容

    var uconf TopLevel
    //將配置檔案內容輸出到結構體中
    err = viperutil.EnhancedExactUnmarshal(config, &uconf)
    //完成初始化,即檢查空項,並賦預設值
    uconf.completeInitialization(filepath.Dir(config.ConfigFileUsed()))

    return &uconf
}

//程式碼在orderer/localconfig/config.go
```

TopLevel結構體及本地配置更詳細內容,參考:[Fabric 1.0原始碼筆記 之 Orderer #localconfig(Orderer配置檔案定義)](localconfig.md)

## 3、初始化日誌系統(日誌輸出、日誌格式、日誌級別、sarama日誌)

```go
initializeLoggingLevel(conf)
//程式碼在orderer/main.go
```

initializeLoggingLevel(conf)程式碼如下:

```go
func initializeLoggingLevel(conf *config.TopLevel) {
    //初始化日誌輸出物件及輸出格式
    flogging.InitBackend(flogging.SetFormat(conf.General.LogFormat), os.Stderr)
    //按初始化日誌級別
    flogging.InitFromSpec(conf.General.LogLevel)
    if conf.Kafka.Verbose {
        //sarama為go語言版kafka客戶端
        sarama.Logger = log.New(os.Stdout, "[sarama] ", log.Ldate|log.Lmicroseconds|log.Lshortfile)
    }
}
//程式碼在orderer/main.go
```



## 4、啟動Go profiling服務(Go語言分析工具)

```go
initializeProfilingService(conf)
//程式碼在orderer/main.go
```

initializeProfilingService(conf)程式碼如下:

```go
func initializeProfilingService(conf *config.TopLevel) {
    if conf.General.Profile.Enabled { //是否啟用Go profiling
        go func() {
            //Go profiling繫結的監聽地址和埠
            logger.Info("Starting Go pprof profiling service on:", conf.General.Profile.Address)
            //啟動Go profiling服務
            logger.Panic("Go pprof service failed:", http.ListenAndServe(conf.General.Profile.Address, nil))
        }()
    }
}
//程式碼在orderer/main.go
```

## 5、建立Grpc Server

```go
grpcServer := initializeGrpcServer(conf)
//程式碼在orderer/main.go
```

initializeGrpcServer(conf)程式碼如下:

```go
func initializeGrpcServer(conf *config.TopLevel) comm.GRPCServer {
    //按conf初始化安全伺服器配置
    secureConfig := initializeSecureServerConfig(conf)
    //建立net.Listen
    lis, err := net.Listen("tcp", fmt.Sprintf("%s:%d", conf.General.ListenAddress, conf.General.ListenPort))
    //建立GRPC Server
    grpcServer, err := comm.NewGRPCServerFromListener(lis, secureConfig)
    return grpcServer
}
//程式碼在orderer/main.go
```

## 6、初始化本地MSP並獲取簽名

```go
initializeLocalMsp(conf)
signer := localmsp.NewSigner() //return &mspSigner{}
//程式碼在orderer/main.go
```

initializeLocalMsp(conf)程式碼如下:

```go
func initializeLocalMsp(conf *config.TopLevel) {
    //從指定目錄載入本地MSP
    err := mspmgmt.LoadLocalMsp(conf.General.LocalMSPDir, conf.General.BCCSP, conf.General.LocalMSPID)
}
//程式碼在orderer/main.go
```



## 7、初始化MultiChain管理器(啟動共識外掛goroutine,接收和處理訊息)

```go
manager := initializeMultiChainManager(conf, signer)
//程式碼在orderer/main.go
```

initializeMultiChainManager(conf, signer)程式碼如下:

```go
func initializeMultiChainManager(conf *config.TopLevel, signer crypto.LocalSigner) multichain.Manager {
    lf, _ := createLedgerFactory(conf) //建立LedgerFactory
    if len(lf.ChainIDs()) == 0 { //鏈不存在
        initializeBootstrapChannel(conf, lf) //初始化引導通道(獲取初始區塊、建立鏈、新增初始區塊)
    } else {
        //鏈已存在
    }

    consenters := make(map[string]multichain.Consenter) //共識
    consenters["solo"] = solo.New()
    consenters["kafka"] = kafka.New(conf.Kafka.TLS, conf.Kafka.Retry, conf.Kafka.Version)
    return multichain.NewManagerImpl(lf, consenters, signer) //LedgerFactory、Consenter、簽名
}
//程式碼在orderer/main.go
```

initializeBootstrapChannel(conf, lf)程式碼如下:

```go
func initializeBootstrapChannel(conf *config.TopLevel, lf ledger.Factory) {
    var genesisBlock *cb.Block
    switch conf.General.GenesisMethod { //初始區塊的提供方式
    case "provisional": //根據GenesisProfile提供
        genesisBlock = provisional.New(genesisconfig.Load(conf.General.GenesisProfile)).GenesisBlock()
    case "file": //指定現成的初始區塊檔案
        genesisBlock = file.New(conf.General.GenesisFile).GenesisBlock()
    default:
        logger.Panic("Unknown genesis method:", conf.General.GenesisMethod)
    }
    chainID, err := utils.GetChainIDFromBlock(genesisBlock) //獲取ChainID
    gl, err := lf.GetOrCreate(chainID) //獲取或建立chain
    err = gl.Append(genesisBlock) //追加初始區塊
}
//程式碼在orderer/main.go
```

## 8、註冊orderer service並啟動grpcServer

```go
server := NewServer(manager, signer) //構造server
ab.RegisterAtomicBroadcastServer(grpcServer.Server(), server) //service註冊到grpcServer
grpcServer.Start()
//程式碼在orderer/main.go
```

server := NewServer(manager, signer)程式碼如下:

```go
func NewServer(ml multichain.Manager, signer crypto.LocalSigner) ab.AtomicBroadcastServer {
    s := &server{
        dh: deliver.NewHandlerImpl(deliverSupport{Manager: ml}),
        bh: broadcast.NewHandlerImpl(broadcastSupport{
            Manager: ml,
            ConfigUpdateProcessor: configupdate.New(ml.SystemChannelID(), configUpdateSupport{Manager: ml}, signer),
        }),
    }
    return s
}
//程式碼在orderer/server.go
```





網址:http://www.qukuailianxueyuan.io/



欲領取造幣技術與全套虛擬機器資料

區塊鏈技術交流QQ群:756146052  備註:CSDN

尹成學院微信:備註:CSDN





網址:http://www.qukuailianxueyuan.io/



欲領取造幣技術與全套虛擬機器資料

區塊鏈技術交流QQ群:756146052  備註:CSDN

尹成學院微信:備註:CSDN

相關文章