hyperledger fabric 結構分析(一)

買賣虛擬貨幣
先前分析程式著眼於細節分析,這樣沒有框架的概念,花了兩天時間分析整理了一下hyperledger fabric的架構設計,分析該程式沒有參照任何資料,如有錯誤歡迎指正,共同進步。

筆者在詳細分析程式前有以下疑問:
1)cli(命令列)客戶端如何傳送命令給peer節點
2)本peer節點如何接收其他節點的資料,接收到資料又如何處理,處理的方式和1又有什麼區別
3)資料是何時又是如何被送入consensus模組
4)consensus模組內部又是如何架構的 為什麼看起來helper executor pbft controller資料夾交至在一起,儲存各自控制代碼,相互呼叫
5)chaincode(鏈碼,簡稱cc)是如何接收到peer對其的操作、訪問的
6)chaincode是如何呼叫fabric api來查詢寫入資料的
7)在閱讀原始碼初始化過程中,peer節點會建立大量server,這些server後續過程我們是如何使用的

注:本人對於資料庫、docker相關知識不是很瞭解,儘量避免關於這兩個部分的介紹以免錯誤的引導讀者。
下面會慢慢的滲透以上涉及的問題。

server :


每個server作用:
adminserver:控制該節點的命運,可以刪除該節點所在的程序。(start stop getstatus )
eventhubserver:peer節點支援客戶端對指定事件進行監聽,例如rejection等。客戶端需要先註冊自己關心的events,當事件發生時trigger 監聽者。
openchainserver:對外提供ledger的訪問介面,涉及getblockchaininfo getblockbynumber等。
devopsserver:負責與cli client對接,外部進行cc操作的入口,deploy invoke query。
chaincodesupportserver:負責與shim/chaincode通訊,chaincode的所有呼叫接收傳送都要與該server資訊互動。
peerserver:該server是一個engine,engine關聯了內部訊息響應實現,同時為周圍peer節點建立client與之通訊。
restserver:該server沒有進行分析,應該是rest介面格式相關。


一級模組分類:

client:  之前建立伺服器與之對應的客戶端,可以理解成其他節點或者cli client等。
protos:  中間層,server與client端 api介面定義
serverprocess:服務響應處理函式,包括各型別的handlemessage。
consensus:  共識模組,目前採用的是pbft noops
chaincode shim:程式碼中shim和我理解的不一致,將chaincodesupport也應該算到shim,該模組的作用是連線peer節點與chaincode的媒介,用shim形容也可。
chaincode:  鏈碼,應用(例如智慧合約)。
db:  資料儲存。
library:  程式碼裡有一個叫做vendor的資料夾,該資料夾裡涉及的功能模組自成一體,例如grpcserver等
api:  chaincode裡面會呼叫peer節點資訊。
crypto:  伴隨著資料加解密。 
ledger:  賬本操作。

該程式碼使用handler觸發模式,在跟蹤程式碼程式時要注意handler物件賦值位置,否則容易找錯handlemessage,這些handler處理函式命名基本相同,容易操作混亂。

下面分析幾個讀者應該最關心的流程:
1)client透過cli執行一條invoke命令
2)某節點傳送給該節點viewchange命令
3)chaincode呼叫api putstatus
4)consensus流程

一、 client透過cli執行一條invoke命令
1)在peer節點初始化的時候 建立devopsserver

serverdevops := core.newdevopsserver(peerserver)  
pb.registerdevopsserver(grpcserver, serverdevops)  

2)devopsserver設定service規範,例如invoke message,呼叫_devops_invoke_handler函式

var _devops_servicedesc = grpc.servicedesc{  
    servicename: "protos.devops",  
    handlertype: (*devopsserver)(nil),  
    methods: []grpc.methoddesc{  
        {  
            methodname: "login",  
            handler:    _devops_login_handler,  
        },  
        {  
            methodname: "build",  
            handler:    _devops_build_handler,  
        },  
        {  
            methodname: "deploy",  
            handler:    _devops_deploy_handler,  
        },  
        {  
            methodname: "invoke",  
            handler:    _devops_invoke_handler,  
        },  
        {  
            methodname: "query",  
            handler:    _devops_query_handler,  
        },  
        {  
            methodname: "exp_getapplicationtcert",  
            handler:    _devops_exp_getapplicationtcert_handler,  
        },  
        {  
            methodname: "exp_preparefortx",  
            handler:    _devops_exp_preparefortx_handler,  
        },  
        {  
            methodname: "exp_producesigma",  
            handler:    _devops_exp_producesigma_handler,  
        },  
        {  
            methodname: "exp_executewithbinding",  
            handler:    _devops_exp_executewithbinding_handler,  
        },  
    },  
    streams: []grpc.streamdesc{},  
}  

3)其中_devops_invoke_handler函式在protos模組,其負責將client接入的資訊傳遞到對應的server模組

func _devops_invoke_handler(srv interface{}, ctx context.context, dec func(interface{}) error) (interface{}, error) {  
    in := new(chaincodeinvocationspec)  
    if err := dec(in); err != nil {  
        return nil, err  
    }  
    out, err := srv.(devopsserver).invoke(ctx, in)  
    if err != nil {  
        return nil, err  
    }  
    return out, nil  
}  

4)在函式在devops服務端程式碼中處理

func (d *devops) invoke(ctx context.context, chaincodeinvocationspec *pb.chaincodeinvocationspec) (*pb.response, error) {  
    return d.invokeorquery(ctx, chaincodeinvocationspec, chaincodeinvocationspec.chaincodespec.attributes, true)  
}  

5)精簡invokeorquery程式碼,d.coord 是peerserver物件,executetransaction 是對應engine的實現方法

func (d *devops) invokeorquery(ctx context.context, chaincodeinvocationspec *pb.chaincodeinvocationspec, attributes []string, invoke bool) (*pb.response, error) {  
resp := d.coord.executetransaction(transaction)  
}  

6)本次請求被封裝成交易struct,該處理是在peerserver中。

func (p *impl) executetransaction(transaction *pb.transaction) (response *pb.response) {  
    if p.isvalidator {  
        response = p.sendtransactionstolocalengine(transaction)  
    } else {  
        peeraddresses := p.dischelper.getrandomnodes(1)  
        response = p.sendtransactionstopeer(peeraddresses[0], transaction)  
    }  
    return response  
}  

7)思考可知,最終這筆transaction是要交給到consensus進行處理,那麼如何傳遞的呢?就在下面p.engine.processtransactionmsg,其中"p"代指peerserver,engine是在建立peerserver的時候指定的engine,而這個engine的handler實現在consensus裡,在實現enginehandler過程中載入了pbft演算法。所以processtransactionmsg函式的實現在consensus模組engine程式碼裡。這樣解決了開始時提出的疑問3)。

func (p *impl) sendtransactionstolocalengine(transaction *pb.transaction) *pb.response {  
    peerlogger.debugf("marshalling transaction %s to send to local engine", transaction.type)  
    data, err := proto.marshal(transaction)  
    if err != nil {  
        return &pb.response{status: pb.response_failure, msg: []byte(fmt.sprintf("error sending transaction to local engine: %s", err))}  
    }  
    var response *pb.response  
    msg := &pb.message{type: pb.message_chain_transaction, payload: data, timestamp: util.createutctimestamp()}  
    peerlogger.debugf("sending message %s with timestamp %v to local engine", msg.type, msg.timestamp)  
    response = p.engine.processtransactionmsg(msg, transaction)  
    return response  
}  

8)從這裡開始進入了consensus內部處理,在這裡consensus模組是單獨分析。

func (eng *engineimpl) processtransactionmsg(msg *pb.message, tx *pb.transaction) (response *pb.response) {  
       err := eng.consenter.recvmsg(msg, eng.peerendpoint.id)  
}  

畫圖說明上述流程:

該圖中沒有體現的一點是在devops server建立的時候將peerserver物件作為構造引數傳入,而peerserver建立的過程就是建立engine的過程,也是載入engine-handler的過程,而engine-handler的實現在consensus模組。圖中直接從devops server 跳入consensus模組有些突兀。

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;