ZStack--工做流引擎

 

在IaaS軟件中的任務一般有很長的執行路徑,一個錯誤可能發生在任意一個給定的步驟。爲了保持系統的完整性,一個IaaS軟件必須提供一套機制用於回滾先前的操做步驟。經過一個工做流引擎,ZStack的每個步驟,包裹在獨立的工做流中,能夠在出錯的時候回滾。此外,經過在配置文件中組裝工做流的方式,關鍵的執行路徑能夠被配置,這使得架構的耦合度進一步下降。java

 

動機數據庫

數據中心是由大量的、各類各樣的包括物理的(好比:存儲,服務器)和虛擬的(好比:虛擬機)在內的資源組成的。IaaS軟件本質就是管理各類資源的狀態;例如,建立一個虛擬機一般會改變存儲的狀態(在存儲上建立了一個新的磁盤),網絡的狀態(在網絡上設置DHCP/DNS/NAT等相關信息),和虛擬機管理程序的狀態(在虛擬機管理程序上建立一個新的虛擬機)。不一樣於普通的應用程序,它們絕大多數時候都在管理存儲在內存或數據庫的狀態。爲了反映出數據中心的總體狀態,IaaS軟件必須管理分散在各個設備的狀態,致使執行路徑很長。一個IaaS軟件任務一般會涉及在多個設備上的狀態改變,錯誤可能在任何步驟發生,而後讓系統處在一箇中間狀態,即一些設備已經改變了狀態而一些沒有。例如,建立一個虛擬機時,IaaS軟件配置VM網絡的常規步驟爲DHCPàDNSàSNAT,若是在建立SNAT時發生錯誤,以前配置的DHCP和DNS頗有可能還留在系統內,由於它們已經成功地被應用,即便虛擬機最後沒法成功建立。這種狀態不一致的問題一般使雲不穩定。編程

另外一方面,硬編碼的業務邏輯在傳統的IaaS軟件內對於改變來講是不靈活的;開發人員每每要重寫或修改現有的代碼來改變一些既定的行爲,這些影響了軟件的穩定性。設計模式

這些問題的解決方法是引入工做流的概念,將整塊的業務邏輯分解成細粒度的、可回滾的步驟,使軟件能夠清理已經生成的錯誤的狀態,使軟件變得能夠配置。服務器

 

注意:在ZStack中,咱們能夠將工做流中的步驟(step)稱爲「流程(flow)」,在如下文章中,流程(flow)和步驟(step)是能夠互換的。網絡

 

問題架構

錯誤處理在軟件設計中老是一個很頭疼的問題。即便如今每個軟件工程師都知道了錯誤處理的重要性,可是實際上,他們仍然在找藉口忽略它。精巧的錯誤處理是很難的,尤爲是在一個任務可能跨越多個組件的系統中。即便富有經驗的工程師能夠關注本身代碼中的錯誤,他們也不可能爲不是他們所寫的組件付出相似的努力,若是整個架構中沒有強制一種統一的,能夠全局增強錯誤處理的機制。忽略錯誤處理在一個IaaS軟件中是特別有害的。不像消費級程序能夠經過重啓來恢復全部的狀態,一個IaaS軟件一般沒有辦法本身恢復狀態,將會須要管理員們去手動更正在數據庫和外部設備中的錯誤。一個單一的狀態不一致可能不會致使任何大的問題,並且也可能甚至不會被注意到,可是這種狀態不一致性的不斷積累將會在某個時刻最終摧毀整個雲系統。app

 

工做流引擎ide

工做流是一種方法,把一些繁瑣的方法調用分解爲一個個專一於一件事情的、細粒度的步驟,它由序列或狀態機驅動,最終完成一個完整的任務。配置好回滾處理程序後,當錯誤或未處理的異常在某一步驟發生時,一個工做流能夠停止執行並回滾全部以前的執行步驟。以建立虛擬機爲例,主要工做流程看起來像:函數

http://zstack.org/images/blogs/scalability/workflow1.png

順序工做流,來源於鏈式設計模式(Chain Pattern,有着能夠預見的執行順序,這是ZStack工做流的基礎。一個流程(flow),本質上是一個java接口,能夠包含子流程,並只在前面全部流程完成後才能夠執行。

public interface Flow {

    void run(FlowTrigger trigger, Map data);

 

    void rollback(FlowTrigger trigger, Map data);

}

Flow接口中,工做流前進到這個流程(flow)的時候run(FlowTrigger trigger, Map data)方法會被調用;參數Map data能夠被用於從先前的流程(flow)中獲取數據並把數據傳遞給後續的流程(flow)。當自身完成時,這個流程(flow)調用trigger.next()引導工做流(workflow)去執行下一個流程(flow);若是一個錯誤發生了,這個流程(flow)應該調用trigger.fail(ErrorCode error)方法停止執行,並通知工做流(workflow)回滾已經完成的流程(包括失敗的流程自身)調用各自的rollback()方法。

FlowChain接口中被組建好的流程表明瞭一個完整的工做流程。有兩種方法來建立一個FlowChain

 

  1. 聲明式

流程能夠在一個組件的Spring配置文件中被配置,一個FlowChain能夠經過填寫一個流程的類的名字的列表到FlowChainBuilder中以被建立。

<beanid="VmInstanceManager"class="org.zstack.compute.vm.VmInstanceManagerImpl">
<propertyname="createVmWorkFlowElements">   
<list>       
<value></value>           org.zstack.compute.vm.VmAllocateHostFlow
<value></value>           org.zstack.compute.vm.VmImageSelectBackupStorageFlow
<value></value>           org.zstack.compute.vm.VmAllocatePrimaryStorageFlow
<value></value>           org.zstack.compute.vm.VmAllocateVolumeFlow
<value></value>           org.zstack.compute.vm.VmAllocateNicFlow
<value></value>           org.zstack.compute.vm.VmInstantiateResourcePreFlow
<value></value>           org.zstack.compute.vm.VmCreateOnHypervisorFlow
<value></value>           org.zstack.compute.vm.VmInstantiateResourcePostFlow
</list>       
</property>   
   
<!--only a part of configuration is showed -->    
</bean>

 

FlowChainBuildercreateVmFlowBuilder=FlowChainBuilder.newBuilder().setFlowClassNames(createVmWorkFlowElements).construct();
FlowChainchain=createVmFlowBuilder.build();

這是建立一個嚴肅的、可配置的、包含可複用流程的工做流程的典型方式。在上面的例子中,那個工做流的目的是建立用戶VM;一個所謂的應用VM具備除分配虛擬機網卡外基本相同的流程,因此appliance VM的單一的流程配置和用戶VM的流程配置大多數是能夠共享的:

<beanid="ApplianceVmFacade"
class="org.zstack.appliancevm.ApplianceVmFacadeImpl">   
<propertyname="createApplianceVmWorkFlow">   
<list>       
<value></value>           org.zstack.compute.vm.VmAllocateHostFlow
<value></value>           org.zstack.compute.vm.VmImageSelectBackupStorageFlow
<value></value>           org.zstack.compute.vm.VmAllocatePrimaryStorageFlow
<value></value>           org.zstack.compute.vm.VmAllocateVolumeFlow
<value></value>           org.zstack.appliancevm.ApplianceVmAllocateNicFlow
<value></value>           org.zstack.compute.vm.VmInstantiateResourcePreFlow
<value></value>           org.zstack.compute.vm.VmCreateOnHypervisorFlow
<value></value>           org.zstack.compute.vm.VmInstantiateResourcePostFlow
</list>       
</property>   
 
<zstack:plugin>   
<zstack:extensioninterface="org.zstack.header.Component"/>       
<zstack:extensioninterface="org.zstack.header.Service"/>       
</zstack:plugin>   
</bean>

備註:在以前的圖片中,咱們把ApplianceVmAllocateNicFlow流程高亮爲綠色,這是建立用戶VM和應用VM的工做流步驟中惟一不一樣的地方。

 

2.編程的方式

 

一個FlowChain還能夠經過編程方式建立。一般當要建立的工做流是瑣碎的、流程不可複用的時候,使用這種方法。

FlowChainchain=FlowChainBuilder.newSimpleFlowChain();
chain.setName("test");
chain.setData(newHashMap());
chain.then(newFlow(){
String__name__="flow1";   
@Override   
publicvoidrun(FlowTriggertrigger,Mapdata){   
/* do some business */       
trigger.next();       
}   
 
@Override   
publicvoidrollback(FlowTriggertrigger,Mapdata){   
/* rollback something */       
trigger.rollback();       
}   
}).then(newFlow(){
String__name__="flow2";   
@Override   
publicvoidrun(FlowTriggertrigger,Mapdata){   
/* do some business */       
trigger.next();       
}   
 
@Override   
publicvoidrollback(FlowTriggertrigger,Mapdata){   
/* rollback something */       
trigger.rollback();       
}   
}).done(newFlowDoneHandler(){
@Override   
publicvoidhandle(Mapdata){   
/* the workflow has successfully done */       
}   
}).error(newFlowErrorHandler(){
@Override   
publicvoidhandle(ErrorCodeerrCode,Mapdata){   
/* the workflow has failed with error */       
}   
}).start();

以上形式使用不方便,由於在流中經過一個map data交換數據,每個流程必須冗餘地調用data.get()data.put()函數。使用一種相似DSL的方式,流能夠經過變量共享數據:

FlowChainchain=FlowChainBuilder.newShareFlowChain();
chain.setName("test");
chain.then(newShareFlow(){
Stringdata1="data can be defined as class variables";   
 
{   
data1="data can be iintialized in object initializer";       
}   
 
@Override   
publicvoidsetup(){   
finalStringdata2="data can also be defined in method scope, but it has to be final";       
 
flow(newFlow(){       
String__name__="flow1";           
 
@Override           
publicvoidrun(FlowTriggertrigger,Mapdata){           
data1="we can change data here";               
StringuseData2=data2;               
 
/* do something */               
trigger.next();               
}           
 
@Override           
publicvoidrollback(FlowTriggertrigger,Mapdata){           
/* do some rollback */               
trigger.rollback();               
}           
});       
 
flow(newNoRollbackFlow(){       
String__name__="flow2";           
           
@Override            
publicvoidrun(FlowTriggertrigger,Mapdata){           
/* data1 is the value of what we have changed in flow1 */               
StringuseData1=data1;               
 
/* do something */               
trigger.next();               
}           
});       
 
done(newFlowDoneHandler(){       
@Override            
publicvoidhandle(Mapdata){           
/* the workflow has successfully done */               
}           
});       
 
error(newFlowErrorHandler(){       
@Override           
publicvoidhandle(ErrorCodeerrCode,Mapdata){           
/*the workflow has failed with error */               
}           
});       
}   
}).start();

 

總結

在這篇文章中,咱們展現了ZStack的工做流引擎。經過使用它,在錯誤發生的時候,ZStack在99%的時間裏能夠很好地保持系統狀態一致,注意是99%的時間裏,雖然工做流大多數時候是一個不錯的處理錯誤的工具,但仍然有一些狀況它不能處理,例如,回滾處理程序運行失敗的時候。ZStack還配備了垃圾收集系統,咱們將在之後的文章對它進行介紹。

相關文章
相關標籤/搜索