容錯技術介紹
常見的增強魯棒性機製的介紹一起其他比較有效地係統檢測缺陷方法。下載本文PDF文檔
容錯的目標是降低或者最小化故障對係統可用性、可靠性、安全性、持續性等得影響。在軟件容錯中,常常使用fault
(缺陷),error
(錯誤),failure
(故障)來表示係統異常的由來。係統缺陷
在某種特定環境下被激活,到至係統產生錯誤
,係統錯誤運行是的是的係統發生某種故障
。
常見方法
容錯按係統級別劃分,分為三個級別,硬件容錯、軟件容錯以及係統容錯。硬件容錯常用的方法包括使用冗餘、多備份技術、增加內存、能源係統冗餘等。硬件錯誤通常能夠夠在兩個物理機上進行隔離處理。軟件容錯主要是正對軟件的魯棒性特征進行增強。常見的方法有checkpoint/restart,recovery blocks,N-Version Programs等。對於係統容錯,設計一個獨立與目標係統的子係統,通過定義定義規則來容忍係統缺陷
。對缺陷
的處理,有以下幾類技術:
- 使用
缺陷避免
技術來避一些錯誤。使用成熟的設計方法論、驗證以及確認方法論、代碼檢查、上線前的演練等。 - 在可能會存在的
缺陷
時,可以選擇缺陷移除
技術,例如測試、集成測試、回歸測試、背靠背測試等; - 或者是在遭遇錯誤是,
缺陷回避
的方式,是的潛在的缺陷
不會被激活。常見技術是通過重新配置係統來達到避免的目標; -
缺陷容忍
技術,係統能夠對缺陷
進行偵測、診斷、孤立、覆蓋、不錯、以及係統恢複。使用以上多種技術混合。
常見的機遇魯棒性的方法
1. Process Pairs
Pocess Pairs:保證係統在某一個時候總能有一個進程來處理客戶的輸入請求。他能處理短暫的軟件錯誤。
框架代碼如下:
//create shadow process; primary return
int ft = backup();//server process.
...
forever{
wait_for_request(Request);
process_request(Request);
}
backup()
實現如下:
int backup(){
int ret, restarts = 0;//count number of child procs
for(;;restarts++){
ret = fork();//clone process
if(ret == 0){//child?
return restarts;
}
while(ret != waitpid(ret,0,0))
;//parent waits for child to terminate
}
}
2. Graceful Degradation
在係統遭遇某個錯誤不能提供完整功能,係統可以降低自己服務能力。
上述backup()
方法中while
循環中waitpid
需要等待到child創建成功。可能永遠不會成功,需要改進。
backup()
實現如下:代碼增加了line 6,7
int backup(){
int ret, restarts = 0;//count number of child procs
for(;;restarts++){
ret = fork();//clone process
if(ret < 0 ){
log("backup: ....");
return -1;//即便fork不成功,也可以返回
}
if(ret == 0){//child?
return restarts;
}
while(ret != waitpid(ret,0,0))
;//parent waits for child to terminate
}
}
3. Selective Retry
重複調用僅當有機會重試成功。例如,內存短缺消失;主要針對突然地高負載資源短缺。它能夠增加資源分配成功的可能性
代碼如下:
//create backup process; primary return
int ft = backup();//server process.
...
forever{
//可能成功,如果條件應許。不成功,繼續forever
if(ft < 0 ){ ft = backup();}//可能死循環;狀態一直打斷。
wait_for_request(Request);
process_request(Request);
}
4. State Handling
在係統不能提供服務後,又要保證client的無狀態屬性。服務端需要持續保存當前的狀態,用於故障後的重試。
//create backup process; primary return
int ft = backup();//server process.
...
forever{
wait_for_request(Request);
get_session_state(Request);
if(num_retries < N){//定義重複次數
process_request(Request);
store_session_state(Request);
}else{
return_error();
}
}
5. Linking Process
有些程序進程見是相互以來的,如果某個進程出錯,其他以來的進程需要偵測到錯誤,明確做相應的處理,通常為結束全部依賴進程。
int fd[2];//pipe fd 管道技術
int backup(){
...
pipe(fd);
ret = fork();
if(ret == 0){
close(fd[1]);//寫端
return restarts++;
}//parent closes other end
close(fd[0]);//讀端
}
6. Rejuvenation
Rejuvenation常用於不可重現得一些問題,比如資源耗盡,線程間的不同調度等。通常是使用以下幾類,1. 周期性,如每周星期天4am;2.基於負載,如沒10000個請求後;3. 預測式,在線監控係統可能預測之後某個時候可能會有crash,提前幹預。
int ft = backup();//server process.
...
for(i = 0; i < N || ft <0; ++i){
if(ft < 0 ){
ft = backup();
if ( ft >= 0 ) i = 0;//reset index
}
wait_for_request(Request);
process_request(Request);
}
7. Checkpointing
周期性的保存進程的狀態。如果需要crash錯誤,回滾到最近保存的狀態。
代碼如下:
pid grandparent = getpid();//after two forks
...
//Simple Verision: for N requests
for(int nxt_ckpt = 0;;nxt_ckpt--){
if(nxt_ckpt <= 0){
pid parent = getpid();
if(backup() >= 0 && grandparent != parent){//fork sucessful and not first fork
kill(grandparent,KILL);
grandparent = parent;
nxt_ckpt = N;
}
}
wait_for_request(Request);
process_request(Request);
}
backup()
同上:
int backup(){
int ret, restarts = 0;//count number of child procs
for(;;restarts++){
ret = fork();//clone process
if(ret < 0 ){
log("backup: ....");
return -1;//即便fork不成功,也可以返回
}
if(ret == 0){//child?
return restarts;
}
while(ret != waitpid(ret,0,0))//死循環
;//fork新的child如果當前child已經crashed了。
}
}
8. Update Lost
在兩個checkpoint點之間係統故障,需要保存客戶請求。在rollback的時候重新處理這些請求。
request_no = 0;
...
for(int nxt_ckpt = 0;;nxt_ckpt--){
//周期校檢,如果校檢點不是最新狀態
if(checkpoint(&nxt_ckpt) == RECOVERY){
//處理log
while((request_no+1,R) in log){
process_request(R);
request_no++;
}
}
wait_for_request(Request);
log_to_disk(++request_no,Request);//保存所有請求
pocess_request(Request);
}
9. Application State Scrubbing
係統狀態的保證可以通過checkpoint以及logging的方式,但是在對特定的運用是,就需要應用自身能保存並回滾狀態。
代碼如下:
int ft = backup();
restore_application_state();//child讀最新的校檢點
...
for(int nxt_scrub = N;;nxt_scrub--){
//周期處理 after N request
...
//only if child process
if(nxt_scrub <= 0 && ft >= 0){
if(save_application_state() >= 0){
exit(0);
}
}
wait_for_request(Request);
pocess_request(Request);
}
10. Process Pools
主要針對資源漏洞的問題,在N個請求後,結束進程。操作係統釋放所有進程資源。或者使用資源預分配技術,按照經驗設定好某些請求資源的需求量,當偵測到程序需要求是,分配資源。
11. Recovery Block
框架結構如下:
ensure { postcondition} by
{primary alternative}
else by {2nd alternative}
else by {3rd alternative}
...
else by {final alternative}
else error;
比如一個處理數組排序:
ensure { A.sort()} by //檢查A是否已經排序完成
{A.my_new_quick_sort();}
else by {A.simple_bubble_sort();}//老式但是魯棒性的方法
else throw exception;//如果所有方法都不滿足,拋出異常
代碼實現如下:
forever{
if(! alternative_left()){
throw exception;
}
/*
生成一個child執行下一個替代方法。
*/
backup(parent);
set_watchdog(timeout);//watchdog處理性能故障
execute next alternative;
/*
替代方法執行成功,滿足判斷條件,結束父進程,返回
*/
if(postcondition()){
reset_watchdog();//成功後重置watchdog
parent.terminate();
return ;
}
exit(0)
}
12. Microreboot
通過解耦係統組件,使得係統在遭遇故障時,組需要重啟需要的組件,而不必重啟整個係統。
核心是組件和數據分離。數據的處理通過持久化存儲的方式保證一致。
最後更新:2017-05-22 13:01:31