這只是一篇非常粗淺的記錄我對(duì)工作流引擎認(rèn)識(shí)的文章。
知道工作流引擎是很久之前了,但是一直都沒(méi)有機(jī)會(huì)嘗試,一是沒(méi)有業(yè)務(wù)上的需要,二是感覺(jué)工作流入門(mén)不容易。最近,項(xiàng)目中用到了一點(diǎn)工作流的東西,雖然我沒(méi)有具體參與,但是了解一下還是好的。于是抽周末兩天時(shí)間讀了一些文章和jBPM以及Activiti的User Reference,本文做一下記錄。SWF指的是Amazon Simple Workflow。
工作流引擎是什么?
“工作流引擎”這個(gè)名字聽(tīng)起來(lái)很?chē)樔耍ā耙妗边@兩個(gè)字眼總是能?chē)樔说模?,我之前就是被嚇著了的程序員中的一個(gè)。百度詞條“工作流引擎”也很難懂。
我們經(jīng)常做一些程序,比如用戶A填一張表,提交后,會(huì)給另一個(gè)用戶B(通常是另一類較色)審核,他們覺(jué)得沒(méi)有問(wèn)題就確定,最后給原來(lái)A用戶發(fā)送一封郵件。在實(shí)現(xiàn)這一類系統(tǒng)時(shí)我們會(huì)設(shè)計(jì)一張任務(wù)表,這個(gè)表中有一列成為Status(狀態(tài)):用戶提交后狀態(tài)是0,審核通過(guò)后狀態(tài)是1,審核沒(méi)通過(guò)狀態(tài)是2。但是這樣設(shè)計(jì)會(huì)有一些擴(kuò)展性的問(wèn)題,比如:
我需要知道某表單的歷史信息:什么時(shí)候由誰(shuí)提交、什么時(shí)候被審核通過(guò)、被誰(shuí)審核通過(guò)等
我需要擴(kuò)展或者改動(dòng)流程:A用戶提交表單后,B用戶希望能收到郵件提醒等
我需要定時(shí)執(zhí)行一些任務(wù):為A用戶提交表單設(shè)置截止時(shí)間,提前截止時(shí)間一天發(fā)送郵件通知
注:這些需求用土鱉的方式是都可以實(shí)現(xiàn),我以前在學(xué)校工作的時(shí)候,買(mǎi)設(shè)備走學(xué)校的采購(gòu)流程系統(tǒng),我就親眼看到一個(gè)工作人員打開(kāi)SQL Server去數(shù)據(jù)庫(kù)中查詢這個(gè)訂單是什么時(shí)候下的。
當(dāng)然,這樣的系統(tǒng)做得多了,就會(huì)對(duì)這些進(jìn)行歸納抽象,比如:
將任務(wù)狀態(tài)表和歷史記錄表抽象出來(lái)成為?TaskService?和?HistoryService?模塊
將定時(shí)任務(wù)以及抽象成?BusinessCalendar?模塊
將用戶管理的部分抽取出來(lái)成為?Identity?模塊
將發(fā)送郵件抽象成?EmailTask?模塊,使用的時(shí)候只需要配置一下收件人和內(nèi)容即可
將需要調(diào)用Java類處理業(yè)務(wù)邏輯功能抽象成?Action,使用時(shí)配置一下具體調(diào)用哪一個(gè)Java類
最后做一個(gè)流程管理的通用界面,能實(shí)時(shí)監(jiān)控流程的執(zhí)行情況
這些也都是jPBM以及Activiti等工作流引擎的核心模塊。
工作流引擎的基本概念
上一節(jié)所述A和B參與的工作流的例子,很好的描述了一個(gè)A和B的工作流程,我們用一種語(yǔ)言來(lái)將這種模式描述出來(lái),jBPM5之前版本用的是jPDL,現(xiàn)在大家都用BMPN,兩者大同小異,都是用XML來(lái)描述流程,也都有可視化設(shè)計(jì)器支持,不過(guò)BMPN是行業(yè)標(biāo)準(zhǔn)。
編寫(xiě)好流程定義(Process Defintion)文件以及相關(guān)的Java類后,就可以部署到引擎中,每一次執(zhí)行稱為一個(gè)流程實(shí)例(Process Instance)。
工作流有版本的概念,jBPM和Activiti上傳一個(gè)新的版本后,版本號(hào)會(huì)增加1,舊版本還沒(méi)執(zhí)行完的流程實(shí)例還會(huì)繼續(xù)執(zhí)行。SWF的版本是個(gè)字符串,隨意指定好了,這樣也很好,字符串名稱更明確。
前面提到工作流抽象出來(lái)的各個(gè)模塊,他們之間也是需要相互交互的,比如?EmailTask?就需要調(diào)用?Identity?模塊來(lái)查找用戶的Email信息、用戶的姓名等。因此需要一個(gè)流程上下文(Process Context)來(lái)協(xié)調(diào)。
最后,流程的各個(gè)Task(或者稱為Activity)之間可能要共享一點(diǎn)信息,jBPM和Actviti都有流程上下文實(shí)例(Process Context Instance)的概念,很像一個(gè)Hash,存放key-value信息,當(dāng)然比Hash更強(qiáng)一點(diǎn)的是,流程上下文實(shí)例支持作用域的概念。
工作流引擎的組成部分
所謂流程定義即使用XML編寫(xiě)的用于描述流程的文件,你需要掌握一些BMPN的知識(shí)。很多工作流引擎都帶有可視化流程設(shè)計(jì)器,但是需要理解的是,背后其實(shí)還是XML,下面是一個(gè)定義了4個(gè)結(jié)點(diǎn)的示例。
? ? ? ? ? ? ? ? ? ? execution.setVariable("message", "Hello")? ? ? ? ? ? ? ? ? ?
定以好流程后,就可以發(fā)布到工作流引擎,工作流引擎負(fù)責(zé)解析這個(gè)XML,并且存到數(shù)據(jù)庫(kù)中。我們通過(guò)API來(lái)啟動(dòng)一個(gè)流程。
對(duì)于苦逼的程序員來(lái)講,API就是一切,不過(guò)經(jīng)過(guò)抽象后的API也不復(fù)雜,基本上還是前面前面一節(jié)所述的概念的抽象。下面是Activiti工作流引擎部署流程定義文件,并啟動(dòng)一個(gè)流程的示例代碼:
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();RepositoryService repositoryService = processEngine.getRepositoryService();repositoryService.createDeployment().addClasspathResource("Demo1.bpmn").deploy();RuntimeService runtimeService = processEngine.getRuntimeService();ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess");
流程啟動(dòng)后,可以通過(guò)API來(lái)對(duì)流程進(jìn)行控制,如觸發(fā)一個(gè)消息等待任務(wù)(receiveTask),甚至是將任務(wù)分配給某個(gè)用戶,獲取某個(gè)用戶的所有任務(wù)等。
List tasks = taskService.createTaskQuery()? ? .taskAssignee("admin")? ? .orderByDueDate().asc()? ? .list();
這段代碼是查詢admin用戶的所有任務(wù),按照截止時(shí)間升序排序,也就是最緊急的放在最前面。
嵌入式部署即將流程引擎嵌入部署于Web應(yīng)用中,這是最容易也是最簡(jiǎn)單的方式,通過(guò)上面的API就可以實(shí)現(xiàn)。
獨(dú)立部署即流程引擎被獨(dú)立運(yùn)行,Web應(yīng)用通過(guò)Rest API或者其他方式調(diào)用流程引擎的接口。Activiti引擎實(shí)現(xiàn)了一套R(shí)est API,SWF也實(shí)現(xiàn)了完整的API結(jié)構(gòu),包括各個(gè)語(yǔ)言的版本。
獨(dú)立部署的好處就是,引擎獨(dú)立運(yùn)行,和外部系統(tǒng)很好的解耦了,外部系統(tǒng)的故障不會(huì)導(dǎo)致工作流引擎的崩潰。
SWF與其說(shuō)是工作流引擎,不如說(shuō)是分布式計(jì)算調(diào)度框架,SWF中只包括Task和History兩部分,甚至是每個(gè)Task之間如果要傳遞一些數(shù)據(jù)的話,都只能通過(guò)第三方存儲(chǔ)(比如Message Queue或者Redis),不過(guò)這也給了編程更大的靈活性,問(wèn)題是這種靈活性是不是非常需要。
一個(gè)SWF由Worker和Decider組成,Worker執(zhí)行實(shí)際的任務(wù),而Decider進(jìn)行流程控制,兩者嚴(yán)格上來(lái)講沒(méi)有區(qū)別,只是所執(zhí)行的任務(wù)不同罷了。每個(gè)Worker和Decider會(huì)定期的去SWF的一個(gè)Task List取下一個(gè)任務(wù)??梢钥闯鰜?lái)這更像是一個(gè)“多線程”的結(jié)構(gòu),而SWF官方網(wǎng)站的Use Case是NASA的火星探索計(jì)劃中需要處理圖片的系統(tǒng),這其實(shí)也是一個(gè)更多側(cè)重于計(jì)算的系統(tǒng),流程反而非常簡(jiǎn)單。
另外,SWF(Simple Workflow)的一個(gè)Workflow不能太復(fù)雜,因?yàn)樗械牧鞒炭刂贫技杏贒ecider,如果太復(fù)雜的話Decider將無(wú)比龐大,給維護(hù)和擴(kuò)展帶來(lái)一定的困擾。