NetBPM工作流的架構(gòu)設(shè)計(jì)及實(shí)現(xiàn)淺析Made by LuBen:2007年08月01日 目錄運(yùn)行組件(Execution Component) 組織架構(gòu)組件(Oorganization Component) 任務(wù)調(diào)度組件(Scheduler Component) 引擎運(yùn)行時(shí)上下文環(huán)境(Execution Context) 讀前的話:由于本文涉及內(nèi)容頗多,若有地方讀來(lái)不很明白,建議先跳過(guò),整體上有個(gè)認(rèn)識(shí)后,再回過(guò)頭來(lái)理解。作者認(rèn)識(shí)有限,若有錯(cuò)誤,歡迎斧正:)原文地址: NetBPM工作流的架構(gòu)設(shè)計(jì)及實(shí)現(xiàn)淺析(轉(zhuǎn)載請(qǐng)保留)
NetBPM接口圖:
下面我們逐步介紹NetBPM的各個(gè)組件,下面是NetBpm組件結(jié)構(gòu)圖: 該組件實(shí)現(xiàn)核心接口IDefinitionSessionLocal,用來(lái)解析、加載流程定義壓縮包,并將其保存到數(shù)據(jù)庫(kù)中。此外,它還提供獲取某個(gè)流程定義,獲取所有有效流程定義列表等和流程定義相關(guān)的方法。 該組件實(shí)現(xiàn)了核心接口IExecutionSessionLocal,它是NetBPM引擎推動(dòng)核心,如前面如述,它主要實(shí)現(xiàn)2個(gè)方法:開(kāi)始一個(gè)流程實(shí)例(Start ProcessInstance)和執(zhí)行一個(gè)活動(dòng)(Perform Activity)。另外,它還提供獲取用戶任務(wù)列表,取消流程實(shí)例等輔助流程運(yùn)轉(zhuǎn)的方法。 該組件實(shí)現(xiàn)接口IOrganisationSessionLocal,
它把所有的組織架構(gòu)信息都聚集在一起,包括Users、Groups和Memberships。運(yùn)轉(zhuǎn)組件在為activitie-state指定執(zhí)行者
時(shí),需要有關(guān)user和group的信息。這些信息將以只讀模式由組織架構(gòu)組件向運(yùn)轉(zhuǎn)組件提供。下面是NetBPM默認(rèn)的組織架構(gòu)組織數(shù)據(jù)模型。在此基礎(chǔ)
上,我們可以很方便的實(shí)現(xiàn)自定義的適合實(shí)際項(xiàng)目需求的組織架構(gòu)組件,以和我們的用戶數(shù)據(jù)庫(kù)或者是LDAP系統(tǒng)相關(guān)聯(lián)。 NOTE:NetBPM源碼中實(shí)現(xiàn)的只是一個(gè)簡(jiǎn)單的組織架構(gòu)模型,但它提供了一種思考方向,我們可以很方便在此基礎(chǔ)上進(jìn)行擴(kuò)展來(lái)實(shí)現(xiàn)滿足切實(shí)需求的自定義組織架構(gòu)組件:) 該組件實(shí)現(xiàn)接口ILogSessionLocal,用來(lái)記錄工作流引擎的操作痕跡,象屬性值的更新(如用戶提交的表單被上級(jí)打回重新填寫,那么就會(huì)出現(xiàn)多次表單數(shù)據(jù),這就是一種屬性更新),委托類的調(diào)用情況等都會(huì)被記錄下來(lái)保存到數(shù)據(jù)庫(kù)中。 該組件實(shí)現(xiàn)接口ISchedulerSessionLocal,
在現(xiàn)實(shí)的業(yè)務(wù)流程中,我們經(jīng)常會(huì)遇到需要定時(shí)觸發(fā)某個(gè)任務(wù)的需求,任務(wù)調(diào)度組件正是作用于此。引擎或者是第三方把需要在某個(gè)時(shí)刻執(zhí)行的任務(wù)信息(包括任務(wù)
執(zhí)行環(huán)境、執(zhí)行時(shí)間等)封裝成Job保存到數(shù)據(jù)庫(kù)中。任務(wù)調(diào)度組件將按照指定的時(shí)間間隔不停的掃描任務(wù)表,根據(jù)執(zhí)行時(shí)間對(duì)比來(lái)執(zhí)行定時(shí)到了的Job。 該組件用來(lái)對(duì)流程定義,流程實(shí)例執(zhí)行情況等進(jìn)行監(jiān)控。(源碼待完善)下面是NetBpm核心項(xiàng)目在Visual 該組件用來(lái)對(duì)流程定義,流程實(shí)例執(zhí)行情況等進(jìn)行監(jiān)控。(源碼待完善) 下面是NetBPM核心項(xiàng)目在Visual Studio解決方案中的源碼結(jié)構(gòu)圖,所有組件都包含在Workflow文件夾下,每一個(gè)文件夾分別對(duì)應(yīng)實(shí)現(xiàn)了一個(gè)核心組件。 flow不知道翻譯為什么好,在JBPM中叫做Token,翻譯為令牌,這里我們就叫做flow吧。它代表activity-states(活動(dòng)節(jié)點(diǎn),見(jiàn)nPdl)的一個(gè)“thread of execution”,相當(dāng)于是一次流程實(shí)例執(zhí)行過(guò)程中在流程定義模板中的令牌(還真難描述清楚,看下面一起理解:))。前面說(shuō)了,一個(gè)流程實(shí)例代表一個(gè)流程定義的一次執(zhí)行。如下圖所示,流程實(shí)例的狀態(tài)可以看成是一顆flows樹(shù)。 當(dāng)開(kāi)始一個(gè)流程實(shí)例后,在start-state(開(kāi)始節(jié)點(diǎn),見(jiàn)nPdl,start
-state實(shí)際上可以看做是一種特殊的activity-state)引擎將自動(dòng)產(chǎn)生一個(gè)名為root的flow。flow中包含了該流程實(shí)例的相關(guān)信
息,如屬性值、流程定義信息、flow所在activity-state的執(zhí)行者等。root flow在遇到fork(分散節(jié)點(diǎn),見(jiàn)nPdl)之前,將更新其帶有的實(shí)時(shí)信息(如執(zhí)行者、屬性值等),這些實(shí)時(shí)信息隨著流程的運(yùn)轉(zhuǎn)而變化。遇到fork后,根據(jù)ForkHandler委
托類,root flow將分散成若干(>1個(gè))forked flow(我們可以把root flow稱為這些forked
flow的父flow)。若是分散為多個(gè),則此時(shí)forked flow將并行運(yùn)行,父flow則暫時(shí)退隱,只至到j(luò)oin(匯聚節(jié)點(diǎn),見(jiàn)nPdl)匯聚,引擎將根據(jù)join定義的JoinHandler委托類來(lái)確定激活父flow的機(jī)制。 NOTE:關(guān)于fork和join機(jī)制,請(qǐng)參考nPdl fork、join小節(jié)一起理解。
attribute用來(lái)表示流程實(shí)例中的變量。一個(gè)attribute-instance(屬性實(shí)例,也就是屬性值)代表一次流程實(shí)例執(zhí)行過(guò)程中對(duì)應(yīng)屬性
的實(shí)例。屬性一般有幾種,一種是activity-state(包括start-state)需要用戶或者第三方來(lái)填寫(更新)的屬性(一般對(duì)應(yīng)用戶
Web界面表單上要填寫的值),一種是角色對(duì)應(yīng)的屬性,還有一些用作標(biāo)識(shí)屬性(用來(lái)存儲(chǔ)某些信息以方便后面的節(jié)點(diǎn)運(yùn)用這些信息處理邏輯判斷)。 因?yàn)閳D片太大,關(guān)于繼承IHandlerContext的接口關(guān)系圖查看點(diǎn)擊這里 ExecutionContext
(ExecutionContextImpl類型的對(duì)象,我們暫且翻譯為運(yùn)行時(shí)上下文環(huán)境:))
包含了引擎在運(yùn)行時(shí)和流程實(shí)例相關(guān)的所有有用信息(上文中提到的flowcontext就是一種ExecutionContext),它在委托類(包括流
程定義壓縮包中程序集中定義的委托類)和流程引擎之間建立起了相互聯(lián)系的渠道,這是非常關(guān)鍵的。如上面ExectutionContext 類圖所示,ExecutionContextImpl實(shí)現(xiàn)了下面這些接口:IAssignmentContext、IDecisionContext、IForkContext、IActionContext、IJoinContext、IProcessInvocationContext、ITaskContext,這些接口都是為匹配特定的委托類而設(shè)計(jì),它們規(guī)范了一種特定的上下文環(huán)境,如IActionContext匹配action類型委托類,IDecisionContext匹配DecisionHandler類型委托類等,而ExectutionContext是所有這些特殊的運(yùn)行時(shí)上下文環(huán)境的一個(gè)綜合。當(dāng)引擎在運(yùn)轉(zhuǎn)組件把ExectutionContext作為參數(shù)傳送到具體類型的委托類時(shí)(關(guān)鍵:這就是委托類和流程引擎建立聯(lián)系的方式),ExecutionContext對(duì)象將“拆箱”成為特殊的Context,如:把ExecutionContext對(duì)象傳給action類型的委托類Run()方法時(shí),ExecutionContext對(duì)象將拆箱為ActionContext對(duì)象以限制其能夠調(diào)用的方法。
如“繼承自IHandlerContext的接口”圖中所示,這些接口都繼承了同一個(gè)接口IHandlerContext。
IHandlerContext是一個(gè)規(guī)范了最基本的委托類處理上下文環(huán)境的接口,實(shí)現(xiàn)該接口的繼承幾口也就都要實(shí)現(xiàn)IHandlerContext中定
義的方法,當(dāng)然每一種繼承它的特定接口又都可以具有其特定的方法。我們先看公共接口IHanlderContext類圖:如上IHandlerContext接口圖所示,這些接口都繼承了同一個(gè)接口IHandlerContext。IHandlerContext是一個(gè)規(guī)范了最基本的委托類處理上下文環(huán)境的接口,實(shí)現(xiàn)該接口繼承接口的類也就都實(shí)現(xiàn)了IHandlerContext中定義的方法。當(dāng)然,每一種繼承它的特定接口又都可以具有其特定的方法。我們先看公共接口IHanlderContext類圖: 大多數(shù)的方法,我們從方法名稱就可以看出其具體作用了,這里重點(diǎn)介紹下GetAttribute()方法和GetConfiguration()方法,這是我們?cè)趯懳蓄悓?shí)現(xiàn)時(shí),要經(jīng)常用到的2個(gè)方法。GetAttribute()用來(lái)獲取流程實(shí)例中的屬性值,包括流程前面處理者產(chǎn)生的屬性值(如用戶填寫表單的值)和前面處理引擎事件中設(shè)置的表示屬性值(注:IActionHandler具有SetAttribute()方法,該方法經(jīng)常用來(lái)標(biāo)識(shí)屬性值,供后面程序邏輯用)等。而GetConfiguration()用來(lái)獲取流程定義中設(shè)置的parameter(參數(shù),請(qǐng)nPdlparameter小節(jié))。 下面再來(lái)看幾種典型的特定上下文環(huán)境接口: 在前面我們一直提到委托類,那么委托類到底是什么呢?這里委托的概念指的不是.NET Framework中delegate,這里可以理解它為“委托、代為處理”這樣的概念就好。
NetBPM被設(shè)計(jì)成通用的流程處理引擎,NetBPM核心執(zhí)行引擎只負(fù)責(zé)處理最基本的業(yè)務(wù)流程邏輯,所有不定的邏輯都被委托給一系列的接口,這些接口稱
作Delegation Interfaces(委托接口),而實(shí)現(xiàn)這些接口的類就是委托類。流程定義約定在什么場(chǎng)合使用什么委托類型,引擎和委托類如何關(guān)聯(lián)也在流程定義中完成。 為了達(dá)到最大的可擴(kuò)展性,流程開(kāi)發(fā)者在流程定義時(shí)可以選擇下面任意一種委托類實(shí)現(xiàn)方式: 正
是方式2這種形式給NetBPM帶來(lái)了極大的靈活性,把只適合于某個(gè)特定流程的的程序邏輯(這些往往占了大多數(shù))以.NET程序集的形式定義在流程定義壓
縮包中,NetBpm通過(guò)流程定義組件將其解析并保存至數(shù)據(jù)庫(kù)。當(dāng)引擎運(yùn)轉(zhuǎn)流程需要調(diào)用委托類時(shí),引擎利用反射技術(shù)實(shí)例化出委托類對(duì)象,然后利用上文介紹
的運(yùn)行時(shí)上下時(shí)環(huán)境(ExecutionContext)建立起委托類和引擎之間的交互渠道,這真是一個(gè)令人興奮的設(shè)計(jì):) 下面是NetBpm中的委托類型(建議和ExecutionContext一節(jié)一起理解): ................///////////////////////////.............是否要添加委托類的例子
包含在一個(gè)流程定義壓縮包中的信息叫做流程定義。NetBpm中,流程是由字段name來(lái)區(qū)分的,也就是說(shuō)引擎根據(jù)流程的name來(lái)判斷兩個(gè)流程是否相
等。在流程定義包中不能指定版本,當(dāng)一個(gè)流程定義被引擎加載后,NetBpm將檢查是否有該流程定義的舊版本。如果有,NetBpm將自動(dòng)設(shè)置該新加載進(jìn)
來(lái)的流程版本為所有存在的舊版本流程定義中最高版本數(shù)目基礎(chǔ)上加1。 當(dāng)調(diào)用運(yùn)轉(zhuǎn)組件獲取流程定義列表方法時(shí),只能獲取到每個(gè)流程的最高版本流程定義。這樣做保證了用戶總是從最新版本的流程定義開(kāi)始一個(gè)流程實(shí)例。當(dāng)新的流程版本加載到NetBpm時(shí),所有正在運(yùn)行的舊版本的流程實(shí)例將保持在原來(lái)流程定義方式下運(yùn)行。 關(guān)于版本的另外一個(gè)方面是委托類。不同版本流程定義的委托類不是共享的,也就是說(shuō)每個(gè)流程在執(zhí)行時(shí)只會(huì)“看到”它自己流程定義的委托類。 NetBpm作為一個(gè)集成平臺(tái),當(dāng)流程運(yùn)行時(shí),肯定會(huì)依賴公司很多其他的IT資源,一旦這些依賴導(dǎo)致流程執(zhí)行時(shí)出現(xiàn)錯(cuò)誤,NetBpm提供了3種解決機(jī)制: 執(zhí)行回滾機(jī)制中,流程實(shí)例將會(huì)被回滾到執(zhí)行activity之前的狀態(tài)。如果是對(duì)NetBpm 調(diào)用Eecution Interface時(shí)發(fā)生流程錯(cuò)誤,所有的流過(guò)的transition(邊)都會(huì)執(zhí)行回滾。 關(guān)于流程定義的詳細(xì)情況,參見(jiàn)nPdl。 NetBpm中用到的框架、組件、工具比較多,它們大都是優(yōu)秀的開(kāi)源項(xiàng)目。如Castle,NHibernate,Log4Net, NVelocity,NUnit,NAnt等,不要被這些框架嚇倒,實(shí)際上,它們僅僅只是“框架”而已:)
NetBpm使用了Castle框架,主要用它來(lái)實(shí)現(xiàn)IOC(控制反轉(zhuǎn)或者說(shuō)依賴注入),以依賴注入的方式加載核心組件,如DBClassLoader,
流程定義組件,運(yùn)行組件,日志組件,組織架構(gòu)組件,任務(wù)調(diào)度組件等。在Web程序啟動(dòng)的時(shí)候,根據(jù)配置文件,所有的核心組件都將注冊(cè)到Caslte
IOC容器中,以后當(dāng)需要使用某個(gè)組件的時(shí)候,只需利用系統(tǒng)提供的ServiceLocator(服
務(wù)加載工具類)從容器中獲取實(shí)例即可。另外,在任務(wù)調(diào)度組件中也有用到Castle的Startable
Facility(注:大家把facility理解為注入性質(zhì)的,對(duì)Castle
IOC內(nèi)核容器的功能擴(kuò)充組件。Castle本身自帶實(shí)現(xiàn)了一些faciltiy,開(kāi)發(fā)者也可以自定義facility),該facitlity主要用來(lái)
自動(dòng)運(yùn)行程序(這里用來(lái)自動(dòng)間隔掃描任務(wù)表,進(jìn)行任務(wù)調(diào)度)。 Castle是.NET平臺(tái)下一個(gè)功能強(qiáng)大的優(yōu)秀開(kāi)源框架,關(guān)于Castle的更多信息,請(qǐng)看Castle官方網(wǎng)站。另外TerryLee的博客中關(guān)于Castle的中文資源也很豐富。 NetBpm中NHibernate組件是作為Castle的一個(gè)facility存在的,它用來(lái)實(shí)現(xiàn)NetBpm數(shù)據(jù)持久層, 并方便的實(shí)現(xiàn)了事務(wù)支持。關(guān)于NHibernate的更多信息,請(qǐng)看NHibernate官方網(wǎng)站,關(guān)于NHibernate作為Castle的facility相關(guān)請(qǐng)看這里。
大家在Demo演示體驗(yàn)的時(shí)候,一定很奇怪,沒(méi)有看到熟悉的.aspx頁(yè)面,而是.rails頁(yè)面,為什么呢?答案就是NetBPM的Web層采用的是
MonoRail框架,而不是我們熟悉ASP.NET框架。MonoRail是Caslte框架下針對(duì)web層編程的一個(gè)子框架,它從Ruby
Rails獲取靈感而來(lái),采用架構(gòu)清晰分工明確的MVC模式。NetBPM采用的使用NVelocity作為頁(yè)面解析引擎的MonoRail,它只是對(duì)
NetBPM核心API的一個(gè)Web界面演示示例,用來(lái)告訴我們?cè)撛鯓訌腤eb層調(diào)用NetBpm
API。所以,雖然MonoRail有其獨(dú)到之處,但是在其被普及并有好用工具支持之前,我們只需簡(jiǎn)單了解下它的運(yùn)行機(jī)制,用以熟悉web層如何利用
NetBpm API,不需要了解它太多。用我們熟悉的ASP.NET實(shí)現(xiàn)Web部分顯然是更好的選擇:)。關(guān)于MonoRail的更多信息,請(qǐng)看這里。 log4net大家一定不陌生了,NetBpm使用它來(lái)記錄系統(tǒng)日志,關(guān)于log4net的更多信息,請(qǐng)看Log4Net官方網(wǎng)站,網(wǎng)上中文資源也很豐富. 單元測(cè)試工具NUnit一直是大家用來(lái)單元測(cè)試的利器。 NetBPM源碼中已經(jīng)建有幾個(gè)測(cè)試工程。關(guān)于NUnit的更多信息,請(qǐng)看NUnit官方網(wǎng)站。 注:移植到.NET Framework 2.0下,可能要更改其版本。
NetBPM的設(shè)計(jì)無(wú)疑是巧妙的,但是現(xiàn)階段的它顯然還不是一個(gè)完美的工作流引擎,缺乏如JBOSS這樣的強(qiáng)大后盾作支持,中途又遇上強(qiáng)敵WF,
NetBPM遠(yuǎn)沒(méi)有其兄弟JBPM風(fēng)光,更新沒(méi)有它快(JBPM已經(jīng)出3.0版本了),獲取的支持也少許多,
但是.NET平臺(tái)下能有這樣一個(gè)優(yōu)秀的開(kāi)源工作流項(xiàng)目是十分可貴的,如果您正在WF中苦苦掙扎,也許,開(kāi)源的NetBPM將帶給您一個(gè)驚喜:) |
|
|
來(lái)自: 夜郎 > 《workflow》