天天看點

我的微型工作流引擎設計

一、前言

提到工作流很多人就會想到OA,的确OA就是典型的工作流的應用,但是工作流并不僅僅局限于OA,工作流應該算是基礎架構軟體,主要用于流程的重組和優化,它有廣闊的應用領域。在java下有很多優秀的開源工作流可以選擇比如activit5、jpbm4等,在.net下卻幾乎找不到令人滿意的工作流引擎可用。當然不是說.net下沒有開源的隻是有些國産開源的但看了代碼後就一點興趣都沒有了,且不說代碼品質如何,還引入了一大堆的東西,想在項目中應用也是非常困難。鑒于此我還是決定自己開發一款.NET微型工作流引擎。

二、基本說明

為什麼叫微型工作流引擎?就是超輕量級,以友善在項目中輕便的使用,比如隻有一個類庫dll,大小也就幾百k到1M左右

    提到工作流很多人就會想到OA,的确OA就是典型的工作流的應用,但是工作流并不僅僅局限于OA,工作流應該算是基礎架構軟體,主要用于流程的重組和優化,它有廣闊的應用領域。在java下有很多優秀的開源工作流可以選擇比如activit5、jpbm4等,在.net下卻幾乎找不到令人滿意的工作流引擎可用。當然不是說.net下沒有開源的隻是有些國産開源的但看了代碼後就一點興趣都沒有了,且不說代碼品質如何,還引入了一大堆的東西,想在項目中應用也是非常困難。鑒于此我還是決定自己開發一款.NET微型工作流引擎。

    為什麼叫微型工作流引擎?就是超輕量級,以友善在項目中輕便的使用,比如隻有一個類庫dll,大小也就幾百k到1M左右,不過我們要先回過頭來看看工作流系統,它實在是太大了,它應該包括:

    1、工作流引擎

    2、工作流設計器

    3、工作流管理系統

    4、表單設計器

       目前來說的我隻實作了核心引擎,流程定義也隻能先在xml中編輯然後讀取到引擎中或者直接定義到資料庫中,但整個流程是能夠正常流轉。至于流程設計器、表單設計器、工作流管理系統這個我有精力了再慢慢開發。這裡我完成的隻是很小的一塊,但是是工作流的核心,可以很友善的嵌入到業務系統中應用。

    引擎主要提供了對于工作流定義的解析以及流程流轉的支援。工作流定義檔案描述了業務的互動邏輯,工作流引擎通過解析工作流定義檔案按照業務的互動邏輯進行業務的流轉,工作流引擎通常通過參考某種模型來進行設計,通過排程算法來進行流程的流轉(流程的啟動、終止、挂起、恢複等),通過各種環節排程算法來實作對于環節的流轉(環節的合并、分叉、選擇、條件性的選擇等)。

三、初步印象

    1、從概念開始解釋估計大家都會看不下去了。我們先拿一個簡單執行個體來看看,建立一個項目,引用我的工作流引擎類庫(Chitu.Bpm.dll,取名為赤兔)。

在項目啟動時配置流程引擎(Global.asax.cs中),如下:   

//初始化流程引擎
BpmConfiguration
    .Instance()
    .Config(@"C:\Configration\BpmConfig.xml")
    .Start();      

在項目中使用時,比如建立流程定義:

//取得工作流上下文
var bpm = new BpmContext()
    .UseTransaction(true)
    .SetActor("蕭秦");

//新增流程定義
bpm.NewProcessDefinition("請假流程")
    .SetXmlFile(@"C:\Definition\demo1.xml")
    .SetCategory("分類1")
    .SetEffectDate(DateTime.Now)
    .SetExpireDate(DateTime.Now.AddDays(180))
    .SetMemo("memo1")
    .Create()  //建立流程定義,隻生成bpm_definition_process表
    .Parse()   //解析xml
    .Deploy(); //釋出流程      

啟動流程:

//啟動流程
var process = bpm.NewProcessIntance("請假流程ID", "蕭秦(業務ID)");   //建立流程執行個體
process.SetVariable("流程變量1", "值1");                     //設定流程變量
process.Start();      

人工任務節點轉交下一步:

//任務完成
var task = bpm.LoadTaskInstance("任務ID");
task.SetVariable("任務變量2", "xx");
task.Signal(); //觸發令牌流轉      

所有的操作都通過Facade模式集中到BpmContext中,操作簡單友善。

2、接下來我們先看看流程定義的XML,以下是我捏造的一個流程,以便把各種節點都放進去了。

<?xml version="1.0" encoding="UTF-8"?>

<process name="樣闆房裝修流程">
  
  <start name="裝修申請">
    <transition to="裝修方案設計" >
      <action class="Namespace.MyActionHandler"></action>
    </transition>
  </start>

  <task name="裝修方案設計">
    <transition to="裝修方案稽核">
      <action script="log.Debug('裝修方案稽核 action test');"></action>
    </transition>
  </task>

  <decision name="裝修方案稽核">
    <transitions>
      <transition to="裝修籌備"     condition-expression="variable.pass == true"></transition>
      <transition to="裝修方案設計" condition-expression="variable.pass != true"></transition>
    </transitions>

    <events>
      <action event="enter" class="enterHandlerClass"></action>
      <action event="leave" class="leaveHandlerClass"></action>
    </events>

    <assignments>
      <assignment owner="{process.starter}"></assignment>
    </assignments>

    <variables>
      <variable type="boolean" name="IsPass" access="read,required"></variable>
    </variables>
  </decision>
 
  <fork name="裝修籌備">
    <transition to="裝修合同簽定"></transition>
    <transition to="等待裝修勞工到位"></transition>
    <transition to="裝修材料預算"></transition>
  </fork>

  <sign name="裝修合同簽定"  necessary="false" async="true">
    <transition to="裝修施工"></transition>
  </sign>

  <wait name="等待裝修勞工到位">
    <transition to="裝修施工"></transition>
  </wait>

  <task name="裝修材料預算">
    <transition to="材料采購子流程"></transition>
  </task>

  <subflow name="材料采購子流程">
    <transition to="裝修施工"></transition>
  </subflow>

  <join name="裝修施工">
    <transition to="施工驗收/付款"></transition>
  </join>

  <auto name="施工驗收/付款">
    <transition to="裝修完成"></transition>
  </auto>

  <end name="裝修完成">  
  </end>

  <events>
    <action event="process-start" class="TestStartHandler"></action>
    <action event="process-end" class="TestEndHandler"></action>
  </events>

  <variables>
    <variable type="string" name="start_id" access="readonly,required"></variable>
    <variable type="string" name="start_person"></variable>
  </variables>
</process>      

定義的根節點為流程(process),流程下為各個任務節點(node),任務節點分為:

start       開始節點

auto       自動節點

task       人工節點

decisioin 決策節點

fork        發散節點

join        聚合節點

sublfow  子流程節點

sign       會簽節點

wait       等待節點

end        結束節點

任務節點下可以包括路由(transition)動作(action)及人員配置設定(assignment)變量定義(variable)

其中action包括幾種類型:1、class 2、script 3、sql 4、webservice 5、expression

在script或expression中可以直接通路process.xxx屬性或task.xxx屬性或variable.xxx簡化了動态c#語句的使用。

當然XML定義中還有很多其它的屬性定義我這裡也沒有都列出來,以後用到了再仔細說。

3、關于資料庫設計,這裡僅僅是流程流轉核心所需要的表,表之間都沒有拉關系

我的微型工作流引擎設計

  四、部分功能剖析

    a、我把它劃分為主要的幾大子產品: 引擎配置、流程定義、執行個體流轉、日志處理、計劃任務

        引擎配置:配置引擎執行個體的資料庫連接配接、日志配置、參數設定等。

        流程定義:利用xml來描述流程,主要定義任務節點,路由、動作事件、變量、人員配置設定等

        執行個體流轉:根據定義運作流程執行個體

        日志處理:輸出日志

        任務計劃:會啟動一個服務,用于處理比如延時啟動,任務過期等

    b、流轉中的關鍵性類設計包括:

        1、流程對象(Process)

        2、工作任務(Task)

        3、路由(Trasition)

        4、令牌(Token)

        5、事件總線與動作處理 (EventBus、ActionHandler)

        6、人員配置設定及委托機制(Assignment、Depute)

        7、流程回退處理(RollbackService)

        8、消息服務(NotifyService)

     c、通常引擎控制流程排程流轉核心的排程算法主要有FSM以及PetriNet兩種,基于排程算法來完成流程的流轉:

        1、FSM(有限狀态機)

        FSM 的定義為包含一組狀态集(states)、一個起始狀态(start state)、一組輸入符号集(alphabet)、一個映射輸入符号和目前狀态到下一狀态的轉換函數(transition function)的計算模型。當輸入符号串,模型随即進入起始狀态。它要改變到新的狀态,依賴于轉換函數。在有限狀态機中,會有有許多變量,例如,狀态機有很多與動作(actions)轉換(Mealy機)或狀态(摩爾機)關聯的動作,多重起始狀态,基于沒有輸入符号的轉換,或者指定符号和狀态(非定有限狀态機)的多個轉換,指派給接收狀态(識别者)的一個或多個狀态,等等。遵循FSM流程引擎通過狀态的切換來完成流程的流轉。

        2、PetriNet

        資訊流的一個抽象的、形式的模型。指出一系統的靜态和動态性質。PetriNet通常表示成圖。遵循PetriNet流程引擎通過令牌來決定流程的流轉。

        我采用的是第二種PetriNet算法。用Token來表示目前執行個體運作的位置,也利用token在流程各個點之間的轉移來表示流程的推進,如下圖所示:

我的微型工作流引擎設計

      令牌流轉邏輯,我把以下類方法都做一個簡化省略了路由選擇及節點處理細節,好讓大家明白令牌的流轉:

//令牌Token類中Signal
public void Signal() 
{
    fromTask.Leave(executeContext);
}

//任務Task類中的Leave
public void Leave(ExecutionContext executionContext)
{
    transition.Take(executionContext);
}

//路由Transition類中的Take
public void Take(ExecutionContext executionContext)
{
    toTask.Enter(executionContext);
}

//任務Task類中的Enter
public void Enter(ExecutionContext executionContext)
{
    Run(executionContext);
}      

至此令牌成功的從一個節點轉移到下一個節點了,令牌的流轉是工作流的關鍵,當然不同的節點處理是有所不同的,其中最複雜的當數發散節點及聚合節點了。這裡就介紹到這裡,不再給大家詳細介紹了。

  d、目前我引擎中實作的主要包括以下功能:

    1、解釋過程定義

    2、控制過程執行個體—建立、激活、挂起、終止等

    3、控制流程排程流轉

    4、自定義動作及事件釋出

    5、流程變量及工作變量處理

    6、任務計劃,比如延時啟動,任務過期等

    7、委托服務,委托代辦

    8、回退服務,回退到任意節點或召回

    9、消息服務,比如認領通知、待辦提醒、催辦消息…

    10、流程任務監控服務

    11、日志處理及曆史記錄

    12、任務配置設定與認領      

    13、參與者組織模型接口

五、總結

目前我的這款工作流引擎還在繼續完善當中,我總結下它的優缺點:

優點:

1、它是一款超輕量極或者說是微型的工作流引擎,而且綠色無污染,它隻有一個dll,大小僅1M左右。

2、它目前支援SQL Server、MySql、Oracle、SQLite、PostgreSql等多種資料庫

3、體型上來說它雖然是微型,但功能上并不算微型,它的設計結合了現代的OA及傳統工作流、基本上可以實作我們大多數的功能需要。

4、它其實是面向開發者設計的,從上面初始印象中的執行個體代碼中大家可以看到,它的接口是很集中、精簡、友好的,讓開發者容易了解而且使用起來更友善簡單。是以它更适合嵌入到項目中開發。

缺點:

1、它現在沒有流程設計器、管理系統、表單設計器等,充其量隻能算是一個類庫,并不是直接拿來就可以使用。

2、目前剛剛完成第一個内部版本,而且目前隻在我們内部項目中使用,是以它不夠成熟,雖然我們會持續的改進和完善。

3、缺乏成功應用的案例。

對于我們自己來說,這些缺點都是我們需要繼續努力的地方,可能還需要大量的時間來完成。目前來說我們還不打算開源,等它慢慢穩定成熟後我們會考慮是不是開源出來。如果大家有好的建議或有哪方面的疑惑我很樂意給大家解答,或者你也在設計開發自己的工作流,我們可以互相交流下。聯系13606021792

繼續閱讀