小弟我的微型工作流引擎设计

我的微型工作流引擎设计

一、前言

    提到工作流很多人就会想到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、缺乏成功应用的案例。
对于我们自己来说,这些缺点都是我们需要继续努力的地方,可能还需要大量的时间来完成。目前来说我们还不打算开源,等它慢慢稳定成熟后我们会考虑是不是开源出来。如果大家有好的建议或有哪方面的疑惑我很乐意给大家解答,或者你也在设计开发自己的工作流,我们可以相互交流下。

34楼丶X線長
期待V1.0版
33楼飞翔的茶叶
支持原创,支持博主分享。,非常适合开发人员使用!
32楼小饼干
好,bucuo,jiayou
31楼海城
又看到你的大作了,你又复活了,希望是个可视化的。
30楼逗比丰
研究过一些国产的开源工作流的确不怎么样,但自己开发又能力和精力都不够,很纠结只有全盘引入了一款国产将就了
Re: 岚岚杰杰
@逗比丰,看看FixFlow,https://code.****.net/kenshinnet/fixflow
29楼sunlovesea
支持楼主,.Net的程序员的福音
28楼梦无尘
楼主终于又出现了,好东西要支持下!
27楼十一月的雨
有兴趣,能否开源啊。博主开篇就说.net没有开源引擎。
26楼Jeffrey.Liang
以前用过WF来实现固定的工作流,貌似用WF来实现动态工作流很困难。希望楼主能把这东西弄好。
25楼muki
也只能听听,用 也用不到 。给不到什么建议 。
24楼科尔顿
科尔顿 表示楼主写的太专业了
23楼junyuz
流程是否支持bpmn2.0规范
Re: 萧秦
@junyuz,引用流程是否支持bpmn2.0规范,现在还没有刻意去支持这些规范,比如没有引入泳道的概念,只是自己使用,以后完善后会考虑去规范化。
22楼Im CY
说句实话,如果是.Net平台,用WF比博主的强多了。,,想开发中国通用的OA流程,满足各个单位,我只能呵呵。,,多说一句,个人认为,学好WF,理解好WF,使用好WF才能正确的路,摸石头过河再造流程引擎基本上都是不归路,没有前途。
Re: 阳光沙滩海岸线
@I#39;m CY,引用说句实话,如果是.Net平台,用WF比博主的强多了。,,想开发中国通用的OA流程,满足各个单位,我只能呵呵。,,多说一句,个人认为,学好WF,理解好WF,使用好WF才能正确的路,摸石头过河再造流程引擎基本上都是不归路,没有前途。,WF太基础了,没点功力去开发配套的东西还真的跑不下去。
21楼牛铁
支持原创,支持博主分享。,非常适合开发人员使用!,请问博主未来是否开源?
20楼科尔顿
纯水机
19楼沈赟
谢谢分享
18楼景裔
关注开源计划
Re: 萧秦
@景裔 @东至 ,等稳定后再考虑开源的事吧,先专心把它做好做完善。
17楼adminica
mark
16楼Ace8793
支持楼主,
15楼dazuo
博主有没有可能提供试用呢?正好有项目需要这样的功能。
14楼通用C#系统架构
java的,这样的技术比较多。
13楼萧秦
目前刚刚完成初步版本,不够成熟没有对外提供试用
12楼jello chen
不错!之前也研究过slickflow来着
11楼东至
关于开源
10楼wangzd19
恭喜复活了!!!!!!
9楼张善友
文章写的很不错,不过开源的.net工作流有很多呢,比如 http://slickflow.codeplex.com/ 和 http://www.cnblogs.com/roadsoft/p/4292622.html
Re: 萧秦
@张善友,是的,这两款工作流出来不久,我都有研究参考下
8楼要有好的心情
mark
7楼索马
东西还不错,不知道是否动态角色的概念?有设计路由和表单的版本控制?还有委托代理,还有拒绝和退回?
Re: 萧秦
@索马,引用东西还不错,不知道是否动态角色的概念?有设计路由和表单的版本控制?还有委托代理,还有拒绝和退回?,关于参与者组织模型我们是通过接口去跟你现有的系统对接的。版本的话只控制到流程定义上,如果修改了路由或节点的话,会生成新的流程定义版本。委托代理,拒绝,召回,回退到指定节点都已实现。
6楼荔城w
设计得不错,希望楼主能做一个完全的示例
5楼Rod_zhu
设计跟net BPM是一个思路
4楼gowk
不给github链接,差评哈哈
3楼郑小琪
不错,不过好已经不算是微型了吧
Re: 萧秦
@郑小琪,引用不错,不过好已经不算是微型了吧,体量上应该算微型吧
2楼56180825
欢迎交流。。这是我做的工作流。。以前的版本,最近修改了界面,我的是以WINFORM当代码生成器根据[表单设计器]和[流程设计器]产生WEBFORM程序,因为webform可以按页修改很保存不影响其他页,效率还不错,连接
1楼IceNewMan
支持一下 期待更详细的描述