[轉]一文理解超級賬本Hyperledger Fabric的架構與坑

導語:本文旨在理解超級賬本Fabric的基本架構。

注:此文轉自http://www.infoq.com/cn/articles/hyperledger-fabric-architecture-trap/

????接觸區塊鏈的童鞋都知道比特幣或者說以太坊,但是了解超級賬本(Hyperledger)的肯定不多。在區塊鏈的世界觀里,一直有公有鏈、聯盟鏈和私有鏈的分別,但后兩者沒有公有鏈引起的反響大,也被很多公有鏈的擁躉所鄙視。

但是我們不應該采取非一即二的思想看待這個問題,世界上沒有任何客觀實體是非黑即白,站的角度不同,結論也不一樣。聯盟鏈(私有鏈)是現有中心化商業團體聯盟之間(團體內部)進行商業活動的手段和渠道,B2B 的業務很難遷移到公有鏈上,不僅僅是性能更是因為商業機密等問題。我相信未來是公有鏈與聯盟鏈(私有鏈)共存,各自都有自己的使用場景,正如廣域網與局域網,互有利弊,各司其職。

拉扯完背景,拖上這篇文章的主角:Hyperledger Fabric,它是由 IBM 帶頭發起的一個聯盟鏈項目,于 15 年底移交給 Linux 基金會,成為開源項目。Hyperledger 基金會的成員有很多大牌,諸如 IBM,Intel,思科等。基金會里孵化了很多區塊鏈項目,Fabric 是其中最出名的一個,一般我們說超級賬本(Hyperledger)基本上指的都是 Fabric。在接下來的篇幅里,我會講解 Fabric 的架構,包括多鏈多通道、賬本設計等等,以及鏈碼的編寫和部署流程,最后再講講遇到的坑和 Fabric 的一些不足。

Fabric 架構

Fabric 的架構歷經了兩個版本的演進,最初接觸的 0.6 版本只能被用來做商業驗證,無法被應用于真實場景中。主要原因就是結構簡單,基本所有的功能都集中在 peer 節點,在擴展性、安全性和隔離性方面有著天然的不足。因此在后來推出的 1.0 正式版中,將 peer 節點的功能進行分拆,把共識服務從 peer 節點剝離,獨立為 orderer 節點提供可插拔共識服務。更為重要的一個變化就是加入了多通道(multi-channel)功能,可實現了多業務隔離,在 0.6 版本的基礎上可以說是質的飛躍。

多鏈與多通道

Fabric 中的鏈(chain)包含了鏈碼(chaincode)、賬本(ledger)、通道(channel)的邏輯結構,它將參與方(organization)、交易(transaction)進行隔離,滿足了不同業務場景不同的人訪問不同數據的基本要求。通常我們說的多鏈在運維層次上也就是多通道。一個 peer 節點可以接入多條通道,從而加入到多條鏈,參與到不同的業務中。如圖所示:

圖中(P1、PN)、(P1、P2、P3)、(P2、PN)組成了三個相互獨立的鏈,peer 節點只需維護自己加入的鏈的賬本信息,感應不到其他鏈的存在。這種模式與現實業務場景有諸多相似之處,不同業務有不同的參與方,不參與該業務,不應該看到業務相關的任何信息。多通道特性是 Fabric 在商用區塊鏈領域推出的殺手锏,當然也不完美,雖然 peer 節點不能看到不相關通道的交易,但是對于 orderer 節點來說,還是所有通道的交易都可以看到,雖然可以使用技術手段分區,但無疑增加了復雜度。

賬本結構

賬本簡單的說,是一系列有序的、不可篡改的狀態轉移記錄日志。狀態轉移是鏈碼(chaincode)執行(交易)的結果,每個交易都是通過增刪改操作提交一系列鍵值對到賬本。一系列有序的交易被打包成塊,這樣就將賬本串聯成了區塊鏈。同時,一個狀態數據庫維護賬本當前的狀態,因此也被叫做世界狀態。在 1.0 版本的 Fabric 中,每個通道都有其賬本,每個 peer 節點都保存著其加入的通道的賬本,包含著交易日志(賬本數據庫)、狀態數據庫以及歷史數據庫。

賬本狀態數據庫實際上存儲的是所有曾經在交易中出現的鍵值對的最新值。調用鏈碼執行交易可以改變狀態數據,為了高效的執行鏈碼調用,所有數據的最新值都被存放在狀態數據庫中。就邏輯上來說,狀態數據庫僅僅是有序交易日志的快照,因此在任何時候都可以根據交易日志重新生成。狀態數據庫會在 peer 節點啟動的時候自動恢復或重構,未完備前,該節點不會接受新的交易。狀態數據庫可以使用 LevelDB 或者 CouchDB。LevelDB 是默認的內置的數據庫,CouchDB 是額外的第三方數據庫。跟 LevelDB 一樣,CouchDB 也能夠存儲任意的二進制數據,而且作為 JSON 文件數據庫,CouchDB 額外的支撐 JSON 富文本查詢,如果鏈碼的鍵值對存儲的是 JSON,那么可以很好的利用 CouchDB 的富文本查詢功能。

Fabric 的賬本結構中還有一個可選的歷史狀態數據庫,用于查詢某個 key 的歷史修改記錄,需要注意的是,歷史數據庫并不存儲 key 具體的值,而只記錄在某個區塊的某個交易里,某 key 變動了一次。后續需要查詢的時候,根據變動歷史去查詢實際變動的值,這樣的做法減少了數據的存儲,當然也增加了查詢邏輯的復雜度,各有利弊。

賬本數據庫是基于文件系統,將區塊存儲于文件塊中,然后在 LevelDB 中存儲區塊交易對應的文件塊及其偏移,也就是將 LevelDB 作為賬本數據庫的索引。文件形式的區塊存儲方式如果沒有快速定位的索引,那么查詢區塊交易信息可能是噩夢。現階段支持的索引有:

區塊編號

區塊哈希

交易 ID 索引交易

區塊交易編號

交易 ID 索引區塊

交易 ID 索引交易驗證碼

Kafka 共識

基于 Kafka 實現的共識機制是 Fabric1.0 中提供的共識算法之一。之所以將 0.6 版本中提供的 PBFT 暫時取消,一是因為交易性能達不到要求;二一點是因為 Fabric 面向的聯盟鏈環境中,因為節點都是有準入控制的,拜贊庭容錯的需求不是很強烈,反而是并發性能最重要。因此,也就有了 Kafka 共識的出現。

一個共識集群由多個 orderer 節點(OSN)和一個 kafka 集群組成。orderer 之間并不直接通信,他們僅僅和 Kafka 集群通信。在 orderer 的實現里,通道在 kafka 中是以 topic 的形式隔離。每個 orderer 內部,針對每個通道都會建立與 kafka 集群對應 topic 的生產者及消費者。生產者將 orderer 節點收到的交易發送到 kafka 集群進行排序,在生產的同時,消費者也同步消費排序后的交易。

那么如何鑒別某個交易屬于哪個區塊呢?Fabric 的區塊結塊由兩個條件決定,區塊交易量和區塊時間間隔。當配置的交易量達到閾值時,無論是否達到時間間隔,都會觸發結塊操作;另一方面,如果觸發了設置的時間間隔閾值,只要有交易就會觸發結塊操作,也就是說 fabric 中不會有空塊。結塊操作是由 orderer 節點中的 kafka 生產者發送一條 TTC-X(Time to cut block x)消息到 kafka 集群,當任意 orderer 節點的 kafka 消費者接收到任意節點發出的 TTC-X 消息時,都會將之前收到的交易打包結塊,保存在 orderer 本地,之后再分發到各 peer 節點。以上過程可由下圖描述:

鏈碼

前面介紹的 Fabric 架構更多的是針對 Fabric 平臺運維工程師,而對更多的應用開發者來說,鏈碼其實才是最重要的。鏈碼(chaincode)是 Hyperledger Fabric 提供的智能合約,是上層應用與底層區塊鏈平臺交互的媒介。現階段,Fabric 提供 Go、Java 等語言編寫的鏈碼,但不管是哪種語言編寫的鏈碼,套路都是差不多的。

所有的鏈碼都繼承兩個接口,init 和 invoke。init 接口用于初始化合約,在整個鏈碼的生命周期里,該接口僅僅執行一次。剩下的 invoke 接口是編寫業務邏輯的唯一入口,雖然只有一個入口,但是可以根據參數傳遞的不同自由區分不同業務邏輯,靈活性很高。比如應用開發者規定 Invoke 接口的第一個參數是合約方法名,剩余的 Invoke 參數列表是傳遞給該方法的參數,那么就可以在 Invoke 接口方法體中根據方法名的不同分流不同業務了。

那么在合約里能夠獲取哪些內容呢?我把合約接口能獲得數據分為三類:

輸入參數獲取。這點很好理解,我們只有知道此次調用的輸入,才能處理邏輯,推導輸出;

與狀態數據庫和歷史數據庫交互。在合約層,我們可以將區塊鏈底層當做是一個鍵值對數據庫,合約就是對數據庫中鍵值的增刪改查;

與其他合約的交互。在合約執行的過程中,可以與其他合約交換數據,做到類似跨鏈的效果。有了這種形式的數據獲取方式,其實就可以將聯系不緊密的業務邏輯拆分為多個合約,只在必要的時候跨合約調用,非常類似于現在提倡的微服務架構。

編寫鏈碼還有一個非常重要的原則:不要出現任何本地化和隨機邏輯。此處的本地化,不是指語言本地化,而是執行環境本地化。區塊鏈因為是去中心架構,業務邏輯不是只在某一個節點執行,而是在所有的共識節點都執行,如果鏈碼輸出與本地化數據相關,那么可能會導致結果差異,從而不能達成共識。比如,時間戳,隨機函數等。這些方法是鏈碼編程的禁地,除非你知道你的用意,否則慎用!

鏈碼部署

在最初接觸 Fabric 的那段時間,我其實是很混淆 peer、通道和鏈碼間的關系的,直到我從頭到尾完成了一次平臺的部署。簡單的說,peer 是一個獨立存在的計算機節點,不管是物理機還是虛擬機,總之是獨立實體。在 peer 沒有加入任何通道之前,是不能夠做任何業務的,因為他沒有業務載體。而通道就是業務載體,是純粹的邏輯概念,可以獨立于 peer 存在,但也因此也沒任何存在的意義了。最后鏈碼就是業務,業務是跑在通道里的,不同的通道即便是運行相同的鏈碼,因為載體不同,可認為是兩個不同業務。peer 是地,通道是路,鏈碼是車,三者相輔相成才能構建一套完整的區塊鏈業務系統。經過以上分析,鏈碼的部署也就如“把大象放進冰箱分幾步”這么簡單了:

創建業務載體通道;

將通道與 peer 節點綁定;

在通道上實例化鏈碼。

從邏輯上來說,就是如此的簡單。當然,在實際的操作過程中,肯定要復雜的多,感興趣的讀者可以對照著官方文檔從頭到尾擼一遍,之后再讀這篇文章,我相信你會有更清晰的認識。

坑與不足

區塊鏈是門新技術,Hyperledger Fabric 是更新的區塊鏈實現,在我們調研實踐的過程中,基本上除了官方文檔以外再也找不到其他資料可供我們參考,我們唯一可行的學習途徑就是:擼源碼!通過擼代碼去了解內部實現機制,出現未知錯誤,直接看源碼反推,使用之路確實艱辛。在使用的過程中,我們也發現了很多坑,一些坑我們可以采用其他辦法繞過,另外的就只能當做是 Fabric 的不足了。

通道的管理:在 Fabric 的設計里,通道其實就相當于賬本的概念。現階段僅僅只有創建,而沒有刪除功能,當然這可以理解,區塊鏈的一個重要特性是不可篡改,如果能直接將整個鏈刪除了那還了得。但是在使用 kafka 共識的過程中,如果數據操作不當,直接在 kafka 中刪除數據,而 orderer 沒有邏輯去處理這種異常刪除,因此會不斷的重試,在達到重試極限后直接崩潰整個進程。因為一個通道的錯誤,影響了整個系統的運轉,這不是一個好的設計;

沒有完善的數據管理方案:在我們的使用場景中,數據增長是很快的,如果使用 CouchDB 作為底層數據引擎,數據更是幾何倍數的爆發。現有的解決方案只能是在云上部署節點,提供可持續擴充的云硬盤,再者使用 LevelDB 替換掉 CouchDB,避免使用模糊查詢。

學習建議

之前因為學習資料的缺失,我們只能通過擼代碼來學習。而現在這個時間點,Fabric1.0 版本已經推出有一段時間了,網絡上已經有了很多學習資料,基本免費開源,此處不做推薦。之后也會具體的針對 Hyperledger Fabric 推出更有側重點的文章,敬請期待。

參考資料:

Hyperledger Fabric 官方文檔 2017:http://hyperledger-fabric.readthedocs.io/en/latest/index.html

A Kafka-based Ordering Service for Fabric 2017:https://docs.google.com/document/d/1vNMaM7XhOlu9tB_10dKnlrhy5d7b1u8lSY8a-kVjCO4/edit

作者介紹

自游,區塊鏈底層架構師。16 年初接觸區塊鏈并全職投入,現供職于某世界 500 強企業做區塊鏈底層研究及 BAAS 平臺搭建。精通區塊鏈底層存儲、共識等技術,職業方向偏重聯盟鏈體系。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容