計算過程中的故障和容災處理

使用Fourinone可以完成大部分分佈式並行計算需求, 但是計算過程中的故障和容災處理是怎麼進行的呢, 這裏詳細分析一下:

[color=red]總的來說,Fourinone框架不會在設計中拋棄錯誤不處理或者容忍錯誤導致框架崩潰,框架通常會捕獲所有的錯誤反饋給開發者去處理,但是框架本身不自作主張,替開發者考慮處理方案,只有這樣框架才能從特定場景中抽象出來,給開發者更靈活的發揮和去滿足各種更復雜業務容錯情況。[/color]

那麼框架究竟關注和不關注哪個層面的故障呢?
並行計算過程中,通常有兩種類型的故障:一種是系統故障引起的計算中斷(宕機和網絡故障), 一種是業務邏輯意義上的錯誤數據。前者是框架關注的,後者是業務邏輯開發者關注的。
系統故障導致網絡斷掉或者宕機,框架會捕獲故障信息並通告,工頭在檢驗工人執行狀態時會獲知,並進行相應的業務上的故障處理,比如重發或者單獨記錄日誌。業務邏輯意義上的錯誤數據,通常在工人的業務實現邏輯裏去判斷,比如計算結果的金額爲負數是一個不符合業務要求的錯誤數據,這個是由開發者去控制,框架不做業務邏輯上的錯誤處理。

針對故障,框架又是怎樣容災的呢?
通常一個典型的分佈式計算結構,由工頭、工人、職介所組成,我們詳細分析一下這幾個角色在故障時各自如何容災:

[color=red]工頭是嵌入式的,他不是一個服務程序,由嵌入他的系統new工頭類並管理他的生命週期,工頭不存在恢復或者容災的概念[/color],就好比我們寫一個helloworld的main函數,很少考慮程序運行到hello, world沒有輸出時就宕機了。但是如果嵌入工頭的系統是一個定時執行的計算任務時,也許要考慮容災,因爲涉及單點問題,可以讓兩個工頭競爭一個分佈式鎖實現([url=http://fourinone.iteye.com/blog/1295279]詳細參考分佈式鎖demo[/url])。

[color=red]工人和職介所是服務程序,如果工人節點故障,職介所會實時感知,工頭分配計算時會獲取到最新活躍工人數量,如果是職介所節點故障,Fourinone實現了領導者選舉機制[/color],會實時切換到備份職介所上([url=http://fourinone.iteye.com/blog/1177036]詳細參考統一配置管理主備領導者切換[/url])。

換句話說,如果一個工人節點在計算開始前發生故障不可用,工頭通過getWaitingWorkers獲取可用工人時不會包括該工人節點,因爲職介所會感知每個工人的可用狀態。

[color=red]如果工人在計算過程中發生故障,框架會進行截獲,然後提前返回計算結果,並設置結果的狀態爲異常。[/color]也就是正常完成計算時:result.getStatus()==WareHouse.READY
計算過程發生故障中斷時:result.getStatus()==WareHouse.EXCEPTION
這樣工頭就可以根據檢查結果的狀態,來做故障時的容災處理。
實際上也可以在工人的doTask實現方法內部捕捉業務異常,由開發者根據程序實現自由決定。

以下demo演示了Fourinone計算過程中的故障容災處理:
FaultCtor:是一個工頭實現,它調用集羣中一個工人doTask執行任務,然後輪詢該結果,判斷結果是否完成或者是否異常,如果結果狀態爲異常,則打印消息。實際上這裏只是簡單演示機制,現實場景中,可以將任務先記錄,工人執行成功後再刪除並跳轉下一個任務,如果異常則繼續重發其他工人執行該任務,或者採用其他故障策略,統一記錄到錯誤日誌,在其他時間再另行排查處理。

FaultWorker:是一個工人實現,它模擬了一個任務執行,睡眠了8秒鐘,然後再製造一個空指針異常。該工人模擬了兩種系統異常狀況,計算過程中可以關閉它,或者等待它運行到空指針異常查看效果,注意這裏doTask本身是不拋出和捕捉異常的,由框架去處理。
運行步驟:
編譯demo的java類:Javac –classpath fourinone.jar; *.java
1、 啓動ParkServerDemo(它的IP端口已經在配置文件的PARK部分的SERVERS指定)
Java –classpath fourinone.jar; ParkServerDemo
2、 運行FaultWorker(傳入端口號參數)
Java –classpath fourinone.jar; FaultWorker 2008
3、 運行FaultCtor
Java –classpath fourinone.jar; FaultCtor

運行後工人進入8秒中“任務執行”,這時可以將該工人進程關閉,然後會查看到工頭界面輸出something wrong about wks[0] result,說明框架已經屏蔽系統故障並反饋到任務結果的異常狀態中,如果8秒中內不關閉,會引發另外一個空指針異常,產生同樣的異常狀態。

完整demo源碼如下:
// ParkServerDemo
import com.fourinone.BeanContext;
public class ParkServerDemo
{
public static void main(String[] args)
{
BeanContext.startPark();
}
}


// FaultWorker
import com.fourinone.MigrantWorker;
import com.fourinone.WareHouse;
import com.fourinone.Workman;

public class FaultWorker extends MigrantWorker
{
public WareHouse doTask(WareHouse inhouse)
{
System.out.println(inhouse.getString("word"));
try{Thread.sleep(8000L);}catch(Exception ex){}
String[] strs = null;
System.out.println(strs.length);
WareHouse wh = new WareHouse("word", "hello ");
return wh;
}

public static void main(String[] args)
{
FaultWorker mw = new FaultWorker();
mw.waitWorking("localhost",Integer.parseInt(args[0]),"faultworker");
}
}


// FaultCtor
import com.fourinone.Contractor;
import com.fourinone.WareHouse;
import com.fourinone.WorkerLocal;
import java.util.ArrayList;

public class FaultCtor extends Contractor
{
public WareHouse giveTask(WareHouse inhouse)
{
WorkerLocal[] wks = getWaitingWorkers("faultworker");
System.out.println("wks.length:"+wks.length);

WareHouse wh = new WareHouse("word", "hello");
WareHouse result = wks[0].doTask(wh);
System.out.println("result:"+result);

while(true){
if(result.getStatus()==WareHouse.READY){
System.out.println("result:"+result);
break;
}
else if(result.getStatus()==WareHouse.EXCEPTION){
System.out.println("something wrong about wks[0] result");
//doTask(wh) again or put wh into log
break;
}
}

return null;
}

public static void main(String[] args)
{
FaultCtor a = new FaultCtor();
a.giveTask(null);
a.exit();
}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章