羅欽凱,倪成章
(1.華中科技大學(xué),湖北 武漢 430074;2.武漢郵電科學(xué)研究院,湖北 武漢 430074)
全行業(yè)大規(guī)模的“上云”需求,和以O(shè)penStack為代表的云計算資源類型的多樣化,極大擴展了云管平臺(cloud management platform,CMP)的服務(wù)領(lǐng)域,亟待實現(xiàn)一種架構(gòu)層面直觀反映復(fù)雜業(yè)務(wù)邏輯的、便于敏捷開發(fā)與持續(xù)集成的CMP開發(fā)方案。單體架構(gòu)已不能滿足軟件即服務(wù)(software-as-a-service,SaaS)層應(yīng)用在高擴展性和高可用性等方面的需要,亟待一種新的設(shè)計架構(gòu)能最大限度地實現(xiàn)高擴展性。
單體架構(gòu)應(yīng)用開發(fā)成型迅速,但隨著線上業(yè)務(wù)需求復(fù)雜多樣化,新需求調(diào)研階段的溝通成本顯著,業(yè)務(wù)邏輯開發(fā)困難,無法滿足應(yīng)用對擴展性和維護性的要求[1],也將導(dǎo)致需求調(diào)研階段需求溝通的成本顯著和上線維護階段可維護性差。從開發(fā)實現(xiàn)業(yè)務(wù)邏輯的痛點出發(fā),通過解構(gòu)工作流核心設(shè)計模型,設(shè)計出工作流微服務(wù)組件結(jié)構(gòu)模型,并基于Spring Cloud微服務(wù)開發(fā)框架給出組件間REST API與流程節(jié)點自由跳轉(zhuǎn)算法,以及CQRS模式數(shù)據(jù)操作等方案。
工作流技術(shù)順應(yīng)了信息時代“異構(gòu)化、松耦合”的發(fā)展趨勢[2]。工作流相關(guān)領(lǐng)域的專家學(xué)者對此做了許多研究。例如,于樂等[3]設(shè)計了云工作流在商業(yè)智能業(yè)務(wù)場景下的SaaS層應(yīng)用;鄧麗等[4]設(shè)計的空間科學(xué)任務(wù)協(xié)同論證平臺論證了復(fù)雜任務(wù)下的協(xié)同性與一致性;李金艷等[5]實現(xiàn)了一種面向協(xié)作的基于角色的柔性工作流訪問控制機制;陳儒等[6]研究了基于事務(wù)規(guī)則驅(qū)動的動態(tài)工作流模型;張型龍等[7]設(shè)計并實現(xiàn)了基于服務(wù)集成的工作流模型。針對工作流技術(shù)應(yīng)用于傳統(tǒng)單體架構(gòu)時功能與業(yè)務(wù)邏輯耦合度較高所帶來的擴展性差、可維護性差等問題,文中提出采用基于微服務(wù)的工作流技術(shù),將面向分布式服務(wù)的工作流[8]應(yīng)用于CMP,以期實現(xiàn)對分布式云計算資源的調(diào)度管理。
工作流技術(shù)沿著以數(shù)據(jù)為中心的流程建模與柔性分布式設(shè)計的方向發(fā)展[9]。業(yè)界提出旨在提供易于理解和易于標識的業(yè)務(wù)流程建模標記(business process modeling notation,BPMN)。衍生的BPMN2.0規(guī)范可以直觀和準確地描述業(yè)務(wù)流程的細節(jié),降低在整個產(chǎn)品生命周期中按需開發(fā)的溝通成本;有利于業(yè)務(wù)邏輯與功能邏輯進一步解耦,降低工作流技術(shù)在復(fù)雜業(yè)務(wù)場景下持續(xù)集成新功能的實施成本[10];它更注重語法定義的通用性和交換格式的標準化,XML格式規(guī)范便于移植在所有遵從BPMN規(guī)范的工作流引擎中解析利用。
微服務(wù)架構(gòu)本質(zhì)上體現(xiàn)的是解耦再封裝[11]。由多個團隊自由選擇合適技術(shù)獨立開發(fā)每個服務(wù),只要遵守統(tǒng)一的系統(tǒng)內(nèi)HTTP型無狀態(tài)通信規(guī)范API即可,降低復(fù)雜系統(tǒng)的設(shè)計門檻,適合團隊敏捷開發(fā);每個服務(wù)均能被獨立部署和驗證,讓持續(xù)部署成為可能,可以顯著提高部署效率;只需要在特定的微服務(wù)組件中增加所需功能,而不影響整體業(yè)務(wù)和其他功能,整個微服務(wù)系統(tǒng)的可擴展性得到提升。微服務(wù)架構(gòu)有別于面向服務(wù)的架構(gòu)(service-oriented architecture,SOA),微服務(wù)架構(gòu)各個服務(wù)間基于端到端的訂閱方式,業(yè)務(wù)流程發(fā)生完全按照業(yè)務(wù)邏輯,當前事件的觸發(fā)只需關(guān)注前置事件即可,需求變化帶來的改變也只面向業(yè)務(wù)上下文而非功能,架構(gòu)設(shè)計相當靈活。
主流的工作流引擎是jBPM5與Activiti。通用性方面,Activiti所采用寬松的Apache License 2.0開源協(xié)議不會在二次開發(fā)商用過程中引起不必要的著作權(quán)糾紛。易用性方面,微服務(wù)架構(gòu)分布式的調(diào)用方式使Spring Cloud開發(fā)框架成為主流,Activiti架構(gòu)繼承自jBPM4,在整合已有jBPM4項目上具有原生優(yōu)勢,這也意味著它對Spring框架的原生支持[12],可以輕松集成基于Spring代理的事務(wù)管理以及基于業(yè)務(wù)邏輯的面向切面編程。
使工作流流程推進模型首先要以滿足以下定義作為前提:
定義1(狀態(tài)轉(zhuǎn)換):從一個給定狀態(tài)出發(fā),只能進入有限狀態(tài)機中其他狀態(tài)的一個子集;服務(wù)隨流程實例的演進,完成調(diào)用其他服務(wù)或是某些需要手動確認觸發(fā)的業(yè)務(wù)流程,流程狀態(tài)從當前節(jié)點轉(zhuǎn)移到下一節(jié)點等待被觸發(fā)。
定義2(事務(wù)控制):下一事件只能在BPMN流程定義模型描述的前置事件完成之后完成,不能跳過任何事務(wù)步驟,否則不滿足事務(wù)性。
定義3(定量描述):BPMN流程定義模型對流程的定性描述還需通過對各類流程數(shù)據(jù)持久化處理使其定量化。
BPMN流程定義模型結(jié)合Petri-Net[13-14]和UML兩種建模思路實現(xiàn)Activiti工作流的流程推進模型。由起點(StartEvent)、終點(EndEvent)、任務(wù)節(jié)點(Activity)與連線(Transition)組成的有向無環(huán)圖(directed acyclic graph,DAG)即為流程定義(ProcessDefination),實例化的流程定義即流程實例(ProcessInstance),實例中流程執(zhí)行體(ProcessExecution)相當于指針。每個實例被加載進工作流引擎都會首先觸發(fā)activityBehaviour()事件找到起點,緊接著會觸發(fā)監(jiān)聽器(executionListener())執(zhí)行起點的end()事件并找到出方向連線事件outgoingTransition();同樣,觸發(fā)監(jiān)聽器執(zhí)行出方向連線的take()事件找到第一個任務(wù)節(jié)點Activity;觸發(fā)監(jiān)聽器執(zhí)行當前節(jié)點的start()事件,為當前節(jié)點設(shè)置屬性、指派任務(wù)或調(diào)用API后,觸發(fā)監(jiān)聽器執(zhí)行當前節(jié)點的end()事件完成當前任務(wù)節(jié)點,并提交事務(wù),將當前的狀態(tài)保存到數(shù)據(jù)庫里。該流程實例會暫停推進,直到外部請求再次觸發(fā)監(jiān)聽器執(zhí)行出方向連線的take()事件找到下一任務(wù)節(jié)點,流程會繼續(xù)向下一步驟推進。圖1是流程推進模型分鏡操作時序圖,每次流程推進都由一次或多次上述步驟組成。
圖1 流程推進模型分鏡操作時序
面向API調(diào)用是Activiti Service的設(shè)計思路。工作流引擎通過對外暴露Service API(見表1),依據(jù)流程定義部署流程實例、配置流程參數(shù)、指派任務(wù)執(zhí)行人或代理人,實現(xiàn)審批、回退、駁回、創(chuàng)建等業(yè)務(wù)功能,完成工作流的內(nèi)在功能邏輯。
工作流引擎未直接暴露的底層服務(wù)API也可以通過ProcessEngine API調(diào)用并借此完成節(jié)點自由跳轉(zhuǎn)等特性功能。
由此衍生出的概念層面三種不同粒度的API封裝模式如圖2所示。
表1 工作流引擎Service API
圖2 不同粒度的API封裝模式
外觀模式(Facade Pattern)下Service API是對功能邏輯的抽象定義,通過調(diào)用對外已經(jīng)暴露的ServiceAPI或重寫的自定義ServiceAPI實現(xiàn)功能邏輯;命令模式(Command Pattern)實質(zhì)上是將不同命令(Command)交由命令執(zhí)行器(CommandExecutor)統(tǒng)一執(zhí)行;攔截器模式(Interceptor Pattern)下,命令模式執(zhí)行當前某個命令的過程,對應(yīng)于在當前命令執(zhí)行前后分別執(zhí)行若干前置或后置攔截器的過程,旨在實現(xiàn)最細粒度的事務(wù)控制。ProcessEngine API在外觀模式層面將工作流引擎內(nèi)部作透明處理,屏蔽底層架構(gòu)實現(xiàn)層面上復(fù)雜的技術(shù)細節(jié),使得調(diào)用服務(wù)不必關(guān)心引擎事務(wù)規(guī)則和流程定義等的具體實現(xiàn)。
微服務(wù)架構(gòu)(見圖3)的設(shè)計理念在于,有編排功能的基于業(yè)務(wù)邏輯的架構(gòu)可以減少耦合,在此基礎(chǔ)上所有的微服務(wù)組件可以訂閱與其業(yè)務(wù)相關(guān)的其他服務(wù)所發(fā)生的事件,進而對事件做出響應(yīng),最終通過REST服務(wù)完成功能。采用API網(wǎng)關(guān)機制聚合系統(tǒng)外部請求和映射系統(tǒng)內(nèi)部服務(wù),內(nèi)部每個微服務(wù)組件提供REST API。請求基于HTTP協(xié)議通過API網(wǎng)關(guān)分發(fā)到目標服務(wù),可以降低外部請求與具體服務(wù)的耦合度,減少通信頻次。采用去中心化的服務(wù)注冊機制,每個微服務(wù)組件的服務(wù)注冊節(jié)點之間沒有主次之分,針對注冊節(jié)點宕機問題提供自動檢測及周期恢復(fù)功能,所有組件通過引用服務(wù)發(fā)現(xiàn)客戶端,在每個組件入口類上添加服務(wù)發(fā)現(xiàn)注解后即可完成服務(wù)注冊。
圖3 CMP微服務(wù)架構(gòu)示意
面向領(lǐng)域模型(domain-oriented model,DOM)可以準確反映業(yè)務(wù)語言,真正實現(xiàn)以業(yè)務(wù)邏輯實體為核心的靈活拓展;基于“Spring+Hibernate”傳統(tǒng)架構(gòu)的J2EE應(yīng)用關(guān)注功能邏輯過于業(yè)務(wù)邏輯,而Spring Cloud依據(jù)領(lǐng)域劃分微服務(wù),以領(lǐng)域模型替代“功能服務(wù)+數(shù)據(jù)表”模型,以并發(fā)的業(yè)務(wù)事件驅(qū)動替代傳統(tǒng)消息總線數(shù)據(jù)驅(qū)動?;谖⒎?wù)的工作流微服務(wù)組件設(shè)計延續(xù)了Spring Cloud“約定優(yōu)于配置”的契約式編程思想,bootstrap.yml配置文件依據(jù)開發(fā)環(huán)境在spring.profiles.active配置項激活目標環(huán)境;Java持久層API(Java persistence API,JPA)將數(shù)據(jù)表名和字段名按照約定映射到方法類,構(gòu)造面向?qū)ο蟮牟樵冋Z句,以非侵入式設(shè)計靈活操作流程數(shù)據(jù)。
架構(gòu)層面實現(xiàn)不同服務(wù)間功能解耦,工作流組件只需暴露API被其他服務(wù)調(diào)用,解決復(fù)雜業(yè)務(wù)邏輯的同時,在后續(xù)持續(xù)集成需求開發(fā)新業(yè)務(wù)功能時,可以在不影響業(yè)務(wù)邏輯和API原有設(shè)計前提下對工作流組件重新設(shè)計開發(fā)。工作流組件如表2所示。
表2 工作流組件概覽
工作流組件運行的具體步驟如圖4所示。
(1)流程管理器訪問BPMN定義模型庫,讀取當前流程定義進入工作流引擎;
(2)由事務(wù)規(guī)則引擎驅(qū)動過程模型的啟動和執(zhí)行,事務(wù)規(guī)則引擎負責(zé)依據(jù)XML設(shè)計規(guī)范解析執(zhí)行過程模型文件;依據(jù)事務(wù)規(guī)則庫中的事務(wù)規(guī)則子模塊對業(yè)務(wù)流轉(zhuǎn)實現(xiàn)事務(wù)控制;
(3)Service API完成當前步驟并持久化流程數(shù)據(jù),并且通過暴露的REST API被其他微服務(wù)調(diào)用;
(4)當前步驟執(zhí)行完成后,工作流引擎處于等待狀態(tài)直到流程監(jiān)聽器相應(yīng)請求被觸發(fā)。
圖4 工作流組件運行示意
CMP中每個微服務(wù)可能是某個事件的生產(chǎn)者、消費者或二者兼有。當租戶客戶端提交云資源訂單申請,租戶服務(wù)端微服務(wù)組件作為當前事件的消費者向工作流組件REST API發(fā)送HTTP請求傳入流程參數(shù);工作流組件作為當前事件的生產(chǎn)者,調(diào)用業(yè)務(wù)層nextStep()方法類,完成持久化和參數(shù)傳遞驅(qū)動當前流程向下推進直到完成當前事件;工作流又作為當前事件的消費者,完成當前事件過程中向OpenStack-MS微服務(wù)組件REST API發(fā)送HTTP請求;OpenStack組件以生產(chǎn)者身份參與當前事件,依據(jù)API參數(shù)分配云硬盤、云主機等云資源。
基于微服務(wù)架構(gòu)的工作流組件將傳統(tǒng)工作流系統(tǒng)中基于每項具體業(yè)務(wù)功能設(shè)計的調(diào)用API設(shè)計為“下一步”(nextStep())、“回退”(rollback())、“開始”(startProcess())和“結(jié)束”(finishProcess())四個REST API,屏蔽每次請求的具體業(yè)務(wù)功能細節(jié);基于Java泛型設(shè)計的動態(tài)實體類滿足無狀態(tài)JSON格式傳入?yún)?shù)并將其代入流程參數(shù)傳遞。如訂單審批工作流被調(diào)用startProcess()方法類的同時,會從API參數(shù)獲取到諸如訂單主題、備注、審批需求等訂單信息傳入當前流程實例的流程參數(shù)在流程中傳遞;針對特性需求,基于Java的繼承特性,使得子類繼承基類,根據(jù)事務(wù)需求靈活擴展開發(fā)。針對流程節(jié)點的自由跳轉(zhuǎn)的業(yè)務(wù)需求,預(yù)設(shè)流程輸出流程不能很好滿足,以下偽代碼實現(xiàn)了對引擎底層未暴露的Command API二次調(diào)用。
算法:rollback()兩步回退算法。
(當前節(jié)點CurrAct;當前節(jié)點出方向OutgoingTrans;當前節(jié)點出方向列表OutgoingTransList;當前節(jié)點原始出方向OriOutgoingTrans;當前節(jié)點入方向CurrTrans;當前節(jié)點入方向列表CurrTransList;上一步節(jié)點PreAct;上一步節(jié)點入方向PreTrans;上一步節(jié)點入方向列表PreTransList;上兩步節(jié)點MultiPreAct;當前節(jié)點新出方向NewTrans;當前節(jié)點新出方向列表NewTransList;)
for(OutgoingTrans:OutgoingTransList){
OriOutgoingTrans=OutgoingTrans;}
OutgoingTransList.clear();
for(CurrTrans:CurrTransList){
PreAct=CurrAct;
for(PreTrans:PreTransList){
MultiPreAct=PreAct;
NewTrans.setDestination(MultiPreAct);
NewTransList.add(NewTrans);
taskService.complete();
historyService.deleteHistoricTaskInstance;}}
NewTransList.clear();
OutgoingTransList.add(OriOutgoingTrans);
傳統(tǒng)單體架構(gòu)應(yīng)用采用CRUD設(shè)計模式(Create/Retrieve/Update/Delete,“增加/讀取/更新/刪除”),持久層操作和查詢一種類型的數(shù)據(jù)通過相同的實體類。微服務(wù)架構(gòu)系統(tǒng)要求業(yè)務(wù)邏輯的實現(xiàn)從數(shù)據(jù)驅(qū)動(Data-Driven)轉(zhuǎn)向任務(wù)驅(qū)動(Task-Driven)和事件驅(qū)動(Event-Driven)。工作流微服務(wù)組件采用“命令查詢職責(zé)分離”(command query responsibility segregation,CQRS)設(shè)計模式,為了使數(shù)據(jù)操作Command(會修改系統(tǒng)狀態(tài)的增刪改操作)和數(shù)據(jù)查詢Query(不會修改系統(tǒng)狀態(tài)的查詢操作)分離。圖5展示了CQRS與CRUD模式的異同。
涉及的CQRS中所有涉及到對數(shù)據(jù)庫的改變均通過發(fā)送Command異步觸發(fā)對應(yīng)事務(wù)性操作,避免了CRUD中操作和查詢同時進行可能導(dǎo)致的事務(wù)沖突;此外面向切面編程思想,通過添加“@Transactional”注解實現(xiàn)每個方法類粒度上的事務(wù)管理,從而最大限度保證對數(shù)據(jù)庫事務(wù)操作的原子性和一致性;針對CMP資源訂單的實時流程狀態(tài)和歷史流程狀態(tài)進行列表分頁展示的需求,基于CQRS設(shè)計模式,針對實時流程數(shù)據(jù)和歷史流程數(shù)據(jù)設(shè)計獨立的服務(wù)調(diào)用REST API,被請求時根據(jù)請求參數(shù)諸如租戶ID、流程ID、執(zhí)行步驟、關(guān)鍵詞等列表分頁展示條件查詢或精確查詢結(jié)果。
圖5 CQRS與CRUD模式的異同示意
文中設(shè)計了CMP工作流微服務(wù)組件,發(fā)揮Activiti工作流引擎整合Spring Cloud微服務(wù)框架的原生優(yōu)勢;系統(tǒng)內(nèi)API調(diào)用采用REST風(fēng)格設(shè)計,最大限度上保證無狀態(tài)請求的效率最大化,降低開發(fā)難度;流程節(jié)點自由跳轉(zhuǎn)有助于有效完成工單訂單審批流程在復(fù)雜業(yè)務(wù)場景下的特性需求;CQRS數(shù)據(jù)操作方案旨在解決復(fù)雜業(yè)務(wù)場景下功能邏輯的高耦合和低效操作下可能產(chǎn)生的誤操作和資源浪費。CMP在OpenStack Pike版本云環(huán)境上驗證了其在云資源調(diào)度管理上的可行性。開發(fā)改進方面,可以通過容器部署微服務(wù)應(yīng)用[15],配置文件采取集約管理,以GitServer的方式按照不同開發(fā)環(huán)境依賴要求,實現(xiàn)配置文件集群部署。