趙會群 任 杰
(北方工業大學信息學院 北京 100144)(北方工業大學大規模流數據集成與分析技術北京市重點實驗室 北京 100144)
自從2008年Nakamoto[1]提出了比特幣這種可以在點對點的交易平臺使用的數字貨幣,其底層技術區塊鏈[2]引起了業界和政府的廣泛關注。區塊鏈技術[3]具有去中心化、不可篡改和數據本地化存儲等特性,為下一代互聯網技術包括匿名在線交易的數字資產提供基礎支持[4-5]。
超級賬本(Hyperledger)是Linux基金會的區塊鏈項目,致力于發展跨行業的商用區塊鏈平臺技術[6-7]。超級賬本項目自創立伊始便吸引了眾多行業的領頭羊,包括金融業、銀行、互聯網行業、運輸業等。其旗下的Hyperledger Fabric子項目是以IBM早期捐獻出的Open Blockchain為主體搭建而成。Hyperledger Fabric是一個帶有可插入各種功能模塊結構的區塊鏈實施方案,目標就是打造成一個有全社會共同維護的開源超級賬本[8]。
對于Fabric區塊鏈而言,其結構中存在兩大類節點:一類是peer節點,一個網絡實體,維護ledger并運行Chaincode容器來對ledger執行read-write操作;另一類是orderer節點,以先到先得的方式為網絡上所有的channel做交易排序,并將交易序列放入block[9]中。對于2019年7月開源的Fabric區塊鏈而言,其排序服務的模式共有kafka、solo和raft三種,其中kafka模式是orderer集群將交易信息發送給第三方kafka[10]服務,由其對交易進行排序;solo模式為單點orderer支撐排序服務;raft模式為orderer集群通過共識機制選舉主orderer來進行交易排序。然而在后兩種排序服務中,存在著一個問題,也就是本文所要解決的問題,同時也是Fabric區塊鏈其本身體系結構存在的問題,即主orderer節點作為排序交易,并打包交易成為區塊的重要節點,一旦主orderer節點受到惡意攻擊或者自主宕機,其主orderer要負責排序的交易數據以及已完成打包但未及時發送出去的區塊都將丟失。雖然交易數據可以重新傳輸,但也造成了大量時間的耗費,然而Fabric區塊鏈系統并沒有對于這樣的體系結構問題作出相應的保障措施。
Fabric區塊鏈的這種體系結構問題,屬于容錯問題。容錯是指如何保證在出現錯誤時系統仍可以提供正常服務[11],通常是以犧牲系統一定的資源(包括時間、存儲、計算等)為代價[12]。容錯問題可以通過容錯技術[13]來解決,容錯技術對于系統而言是重要的可靠性保障手段[14],容錯技術主要包含三個內容:故障診斷技術、故障屏蔽技術、動態冗余技術[15]。將容錯技術應用到Fabric區塊鏈中仍然存在著一些挑戰。首先,在區塊鏈領域中目前還沒有容錯技術的使用;其次,在一般的含有容錯技術的系統中故障診斷[16]的作用都只是利用心跳機制[17-18]檢測節點是否存活,缺少檢測節點是否遭受惡意攻擊的機制。
本文針對Fabric區塊鏈的容錯問題,設計了備用orderer節點對主orderer節點實時檢測并備份恢復其業務的容錯算法。在原Fabric 區塊鏈系統的基礎之上增加了可靠性機制,力爭使原系統在可靠性方面得到增強。
目前還未有區塊鏈領域的容錯機制,因此本文的相關工作主要選取的是容錯機制在其他領域的應用。
趙鎮輝等[18]對CLAIMS這個內存數據系統引入了容錯機制,并提出了Fail-fast、Fail-over,以及Fail-back三種算法,即Fail-fast算法實現系統中快速發現故障節點并標記,Fail-over算法則是在標記故障節點之后,實現對受影響任務的重啟,而Fail-back算法則是實現對故障節點中內存狀態的恢復。對于CLAIMS系統的容錯機制,測試故障節點只是通過簡單的心跳機制來完成,并未對其有惡意攻擊的測試方案。
Nagarajan等[19]設計一種名為Screwdriver的異常檢測工具,主要的作用就是在系統被注入如高CPU、高內存利用率,磁盤已滿和網絡占用率高等故障之后,可以通過異常檢測模塊發現錯誤,之后利用通知服務模塊生成測試報告。Screwdriver工具并未對故障模塊有恢復保障機制,而只是測試異常。
孔超等[20]對Bigtable、HBase、Dynamo、Cassandra,以及PNUTS五個典型的NoSQL系統的容錯機制及其實現進行分析與對比,使用的故障檢測都只是單純的心跳機制,并且在節點發生故障的時候,也是通過冗余的資源完成故障恢復,使系統具備容忍故障的能力[21],即一個master節點,三個副本節點來進行備份。同樣,在此故障檢測中并未含有惡意攻擊的測試流程。
劉添添[22]對移動Agent系統提出了一種基于消息機制和日志記錄的容錯協議,即利用了容錯技術中的冗余技術,實現了一個Backup agent對Agent進行故障檢測及故障恢復。但此容錯機制只是對于主節點進行故障檢測和數據備份,一旦主節點故障,并沒有及時的服務恢復機制。
段澤源[23]對大數據流式處理系統的容錯機制做了研究,其系統主要依靠Zookeeper這種較成熟的分布式系統協調系統利用心跳機制對節點運行狀態進行檢測,之后使用定期同步節點信息到數據庫的方式冗余節點信息,以便在節點失效重啟時恢復節點數據。此容錯機制的設計不足與文獻[18]一樣,都是缺乏惡意攻擊的測試手段。
李軍國[24]對基于軟件體系結構的容錯機制動態配置技術做了深入研究,其中的錯誤檢測模塊就劃分多類,有心跳探測、異常捕獲、接受性測試等,恢復模塊也有多種,如定向器、狀態重置器,以及分發器和收集器等,其整個容錯模塊的調用則是通過一個容錯管理服務。該文將這種可動態調整的容錯機制應用到了北京大學的反射式JEE應用服務器PKUAS中。這種動態調整的容錯機制的確考慮到了多種故障問題的容錯機制調整策略,但其并未將其容錯機制應用到區塊鏈系統中。
本文設計的容錯機制不僅讓其首次應用到Fabric區塊鏈系統中,而且在其主節點故障檢測模塊中,在傳統的心跳探測之上添加了惡意攻擊的測試,為主節點增加了一道保障,并可以通過測試結果快速判斷出主節點是否故障,若主節點故障之后會立即進入服務恢復模塊,啟動備用節點恢復主節點丟失的數據,繼續保證系統正常運作。
本文在Fabric中為負責排序服務的orderer,增加了備用orderer,讓備用orderer主動監聽主orderer的運行狀態,并發送一些對應的特征測試用例,通過監聽狀態以及這些特征測試結果,從而判斷出主orderer是否宕機或者被惡意攻擊。
算法1安全可靠性測試算法
輸入:主orderer的IP。
輸出:主orderer的運行狀態。
主orderer端:
1. 讀取本地IP,并監聽本地端口port;
2. REPEAT
3. IF 接收到連接請求 THEN
4. 建立連接;
5. IFreceiveMessage==特征測試用例THEN
6.Send(特征測試結果);
7.Sleep(t1);
8. ELSE
9. 等待備用節點連接;
備用orderer端:
1. 獲取主orderer IP;
2. REPEAT
3. IF 連接成功 THEN
4.Send(特征測試用例);
5.Receive(特征測試結果);
6. IF(測試結果==惡意攻擊特征) THEN
7. 主orderer被惡意攻擊,MasterOrdererStatus=“Attack”;
8. UNTIL !MasterOrdererStatus;
9. ELSE
10. 主orderer 工作正常;
11. ELSE
12. 停止等待t2,嘗試連接;
13. IF 連接失敗 THEN
14. 主orderer宕機,MasterOrdererStatus=“Down”;
15. UNTIL!MasterOrdererStatus;
通過安全可靠性測試算法,備用orderer可以獲取主orderer的運行狀態,一旦判斷出主orderer故障,那么備用orderer就要對其進行業務數據方面的恢復,后面的算法就是針對其做的可靠性保障,將其分為數據同步備份算法和服務恢復算法。
算法2數據同步備份算法
輸入:主orderer的業務數據。
輸出:備份主orderer業務數據的數據庫。
主orderer端:
1. REPEAT
2. IF 緩存消息數量>MaxMessagesCountTHEN
//數量大于設置值
3. IF 產生新的消息隊列 THEN
4.TimeBatch:=time.Now();
5.Send(batch,TimeBatch);
//發送消息隊列給備用orderer
6. IF 產生新的區塊 THEN
7.TimeBlock:=time.Now();
8.Send(block,TimeBlock);
//發送區塊給備用orderer
9. IF 消息處理時間>BatchTimeOutTHEN
//處理時間大于設置值
10. IF 產生新的消息隊列 THEN
11.TimeBatch:=time.Now();
12.Send(batch,TimeBatch);
13. IF 產生新的區塊 THEN
14.TimeBlock:=time.Now();
15.Send(batch,TimeBlock);
備用orderer端:
1. REPEAT
2. IFReceive(batch,TimeBatch)==trueTHEN
3.envelope:=toByte(batch);
4.envelopeDB(TimeBatch,Msg);
//levelDB數據庫,主鍵:TimeBatch值:envelope
5. IFReceive(block,TimeBlock)==trueTHEN
6.block:=toByte(block);
7.blockDB(TimeBlock,block);
//levelDB數據庫,主鍵:TimeBlock,值:block
8. IFMasterOrdererStatus!=null THEN
9.TimeEnd:=time.Now();
10. IFMasterOrdererStatus==“Down” THEN
11.TimeKey:=TimeEnd-t1-t2;
//t1、t2分別為算法1的休眠時間和等待時間
12. ELSE
13.TimeKey:=TimeEnd-t1;
14. UNTIL !MasterOrdererStatus;
算法3服務恢復算法(數據庫遍歷查找)
輸入:主orderer故障指令,數據庫查找時間TimeKey。
輸出:需要還原的備份數據。
備用orderer端:
1.ID:=SelectNewOrdererID();
//共識算法外接函數,從多個備用orderer中選新主orderer
2.SendTakeOverCmd(ID);
//將新主ID,發送服務接管函數,建立與peer之間的通信
3.db:=OpenFile(“DB”);
//打開DB數據庫即envelopeDB 或blockDB數據庫
4.dbiter:=db.NewIterator;
//建立數據庫迭代器
5.i:=0;
6. REPEAT
7. IFdbiter.key>=TimeKeyTHEN
8.Copy(tmpStruct[i].key,dbiter.key);
//將備份數據暫存結構體數組中
9.Copy(tmpStruct[i].value,dbiter.value);
10.i++;
11. UNTILdbiter.next==null
12. 將tmpStrcut數據發送給peer;
算法4服務恢復算法(建立B樹索引查找)
輸入:主orderer故障指令,數據庫查找時間TimeKey。
輸出:需要還原的備份數據。
備用orderer端:
1.ID:=SelectNewOrdererID();
2.SendTakeOverCmd(ID);
3.db:=OpenFile(“DB”);
4.dbiter:=db.NewIterator;
5.BTree:=newBT(M);
//構建一個空的B樹, 定義B樹的階數為M
6. REPEAT
//構建B樹索引
7.Flag:=BTree.Search(dbiter.key);
//查找此key是否已經存在B樹中
8. IFFlag==falseTHEN
9.BTree.Insert(dbiter.key);
//將key值插入索引
10. ELSE
11. CONTINUE;
12. UNTILdbiter.next==null
13.i:=0;
14. REPEAT
15. REPEAT
16. UNTILi>BTree.num
//i小于結點內關鍵字的個數
17. IFTimeKey 18.Copy(tmpStruct[i].key,BTree.data[i].key); 19.Copy(tmpStruct[i].value,BTree.data[i].value); 20.i++; 21.BTree=BTree.child[i]; 22. UNTILBTree==null 23. 將tmpStrcut數據發送給peer; 對于服務恢復算法,首先是從兩個備份數據庫中分別獲取數據,由于數據庫的主鍵存儲是用時間戳來存儲的,所以本文設計從TimeEnd-t1-t2(t1、t2來自算法1)時刻開始獲取數據,將數據暫存在結構體數組中。在與peer建立連接之后,對于區塊數據便將其直接發送給peer,但是對于消息序列數據需要調用orderer的CreatNextBlock()函數,打包成區塊,再發送給peer。所以本文針對時間戳查找做了兩種服務恢復算法,一種是數據庫遍歷查找服務恢復,另一種是為數據庫建立B樹索引查找服務恢復。 本文實驗的硬件環境是一臺操作系統為Linux并配置有8 GB內存、3.40 GHz CPU的電腦;軟件環境是基于Fabric1.0版本,在官方給出的examples/e2e_cli案例基礎之上,進行二次開發,并且e2e_cli使用的共識模式solo,在原先的2個peer組織即4個peer節點,1個orderer組織即1個orderer節點基礎之上增加到4個orderer節點以及7個peer節點,指定其中主orderer和備用orderer。 本實驗所需要的數據主要是peer之間的交易信息,利用peer之間進行頻繁的轉賬操作,生成交易信息,從而提交到主orderer節點,為備用orderer的備份提供數據。 本文選取的數據集來源于國泰安數據中心[25],數據集如表1所示。 表1 中航地產證券的交易數據(部分) 本文使用的均為證券交易數據,共用了10組數據集,即十家企業證券在2009年至2010年的交易數據,每組數據集的數據量的大小如表2所示。 表2 數據集的數據量大小 首先利用實驗數據模擬peer之間的正常轉賬場景,其中peer1作為購買證券的客戶,peer2作為一家證券企業,通過cli工具執行兩個peer之間的轉賬函數。以表1中每一行的交易金額作為每一次執行的轉賬金額,從而為主orderer產生需要排序、打包的交易數據,同時也會讓備用orderer的備份功能運轉起來,從而為備用orderer恢復主orderer業務做好鋪墊。 其次是對主orderer的惡意攻擊的場景模擬,主要是Linux系統中最常見的兩種病毒攻擊。 第一種模擬主orderer受到Ramen蠕蟲[26]攻擊,即讓主orderer滿足如下要求: (1) 存在/usr/src/.poop目錄。 (2) 存在/sbin/asp文件。 (3) 本地端口27374被打開。 第二種模擬主orderer被Rootkit病毒[27]攻擊,即讓主orderer滿足如下要求: (1) 網絡占用率達到90%以上。 (2) CPU占用率達到100%。 在這種制造故障的情形下,可以觀察備用orderer能否正常運轉業務恢復。 在Fabric網絡啟動成功之后,實驗過程分為以下8步。 步驟1查看備用orderer與主orderer的日志來確定它們之間是否建立連接、是否開始檢測,對于測試算法中需要的特征測試用例,依據需要針對的惡意攻擊特征去生成。本實驗模擬的惡意攻擊手段是Ramen蠕蟲和Rootkit病毒,因此測試用例的生成就要依據實驗場景中說明的兩種病毒的特征去生成。 步驟2將peer組織中的節點和主orderer以及備用orderer加入同一通道,之后就是進行chaincode安裝以及實例化,目的是制定peer之間正常交易的規則,之后就是實現peer之間的轉賬操作,為主orderer提供交易數據。 步驟3通過查看主orderer的實時日志,可以知道其已經接收到這些交易數據,并且已經通過接收時間的先后生成了消息序列,那么查看備份節點實時日志,是否同步備份好這些消息序列,可以通過打印envelopeDB和blockDB數據庫的數據量來確定是否同步備份好主orderer的數據。 步驟4利用3.3節的病毒模擬注入,同時在算法1中t1=3 s之后,備用orderer立刻檢測出主orderer受到惡意攻擊,并記錄TimeEnd。 步驟5備用orderer立刻停止對主orderer的檢測,使用外接函數SelectNewOrdererID()獲取新主orderer的ID,然后將此ID傳入SendTakeOverCmd()函數,使用此函數建立與peer之間的通信,從而替換舊主orderer。 步驟6新主orderer中的服務恢復算法立刻使用TimeEnd-t1的時間作為還原數據的TimeKey,如果是主orderer自主宕機之后,就需要再減去t2。對于兩個服務恢復算法,需要比較其恢復時間,因此執行步驟7、步驟8并對比其兩種恢復時間。 步驟7在兩種沒有索引的數據庫envelopeDB和blockDB中查找,遇到key值比TimeKey大的數據,就存儲起來,并且記錄此方法的開始時間和結束時間,計算總的運行時間,最后將需要還原的envelope數據劃分為區塊之后傳遞給peer,而需要還原的block則直接發送給peer。 步驟8通過建立B樹索引的數據庫來還原數據,計算此方法的運行時間。 利用10組數據集進行了10組實驗,每一組數據進行一組實驗,以下所有數據圖中每組的接管反應時間、恢復數據量、數據庫查找時間均為此組數據集多次重復實驗的均值,模擬了病毒攻擊,并統計了備用orderer接管主orderer業務的反應時間,反應時間基本在4 s左右,如圖1所示。 圖1 備用orderer的接管反應時間 統計了實驗中數據恢復的數據量,如圖2所示。 圖2 恢復的數據量 可以看出,不同的數據集恢復的數據量大小會有所差異,主要是因為數據量的恢復取決于主orderer故障期間接收到的數據量。 統計了區塊和消息序列數據庫使用遍歷查找的服務恢復算法所用的時間,如圖3所示。 圖3 數據庫遍歷查找恢復時間 圖3刻畫了在數據庫中遍歷查找備份數據所消耗的時間,可以看出數據集2、4、9普遍低一些,這是因為這三個數據集數據量較少導致數據庫中備份數據較少。 與圖3使用遍歷查找算法對應的是使用B樹索引查找的服務恢復算法,其恢復過程所用的時間如圖4所示。 圖4 數據庫B樹索引查找恢復時間 同樣地,圖4刻畫了在建立B樹索引的數據庫找備份數據所消耗的時間,并且普遍低于圖3的遍歷查找時間消耗,這也說明了服務恢復算法中建立B樹索引查找還是較優的。 本文為Fabric區塊鏈增加了容錯機制,并且在利用心跳機制的診斷方法之上增加了惡意攻擊的測試算法,進一步完善了故障診斷技術,更加保障了主orderer的安全可靠性。當主orderer遇到故障之后造成主orderer未及時傳輸出去的交易數據丟失的問題,以及無法繼續負責交易數據的排序及打包成區塊的工作,做了相應的備用orderer恢復主orderer的保障機制。通過實驗,本文算法的可用性得到了初步的驗證。 由于本文提出的容錯機制是首次在區塊鏈中嘗試,所以仍存在一些技術難題,如:主orderer故障之后peer對于orderer的連接還未及時切換到新主orderer上,仍繼續會向舊主orderer發送數據,怎么來保證這一段時間內數據的不丟失將是今后繼續研究的方向。3 實 驗
3.1 實驗環境
3.2 實驗數據


3.3 實驗場景
3.4 實驗過程
3.5 實驗結果




4 結 語