EOS工作流引擎工作原理(一)

EOS 工作流引擎工作原理

作者: dogreet  

1.  工作流基础知识

……

2. EOS 工作流引擎工作原理

   本文是我在工作之余写的一点我对 EOS 工作流的了解,我的理解不一定全是对的,可能会与引擎的真正的面目有出入。所以只能提供给大家一点参考。

2.1. EOS 工作流引擎核心调度算法

   EOS 工作流最重要的组成部分是它的核心调度算法,在我们没有深入研究它的工作原理之前我们认为它的工 作原理是在工作项,活动和流程实例对象上加了一些标志位来驱动流程的运转。认为其引擎完全是个由数据库来驱动流程的引擎(安徽二期的工作流平台好象就是以 库表来驱动流程的运转),其实它是由事件来驱动流程运转的引擎,数据库只是把引擎运转前后的状态持久化。在我近来在工作之余对其引擎的工作原理进行跟踪才 弄明白在 EOS 帮助文档上介绍的 “ 事件驱动 ” 的工作流引擎。

2.1.1. EOS 工作流引擎的事件类型

http://gocom.primeton.com/uploads/FCKUserFiles/Image/01%2866%29.jpg

  以上的每个事件都是原子的不可分割的。其中一系列事件的集合通过 EOS 引擎事件调度机制实现我们平时在工作中经常遇到的如启动流程,结束工作项等等。(在事件类型类中 EOS 定义了 29 种事件,但在事件工厂类中 EOS 定义了 26 种类型。)

1.1.1. EOS 工作流事件调度机制

   EOS 事件的调度服务是在工作流引擎初始化时通过服务工厂类加载到内存中 ( ServiceFactory.initEventService() )。用户可以通过服务工厂类( ServiceFactory )取得 JVM 的唯一事 件服务实例进行事务调度。所有的事件程序入口都是事件类( EventService ),这个类其实是个接口,其有两个实现类,一个是单线程的实现类 SingleThreadEventService (在实现代码中其实不是单线程,而是单例的对象),一个是多线程的实现类 MulThreadThreadSvc ,(其实现方式不在这里详细说明,多线程的类后面又跟了一大堆的线程池实现代码),在事件服务类中有一个属性类是 WFEventDisposer ,这个类包含了事件的注册,事件的发布,事件的注册是一个静态代码块实现的。注册了上节描述的 29 种事件,其实就是把相应 的事件代码注册到相应的处理类,事件处理类共用 5 个 ( ProcessScheduler , ActivityExecuter , ExceptionHandler , WorkItemHandler , ApplicationHandler ), 对应事件代码的前 5 个数字;共有事件的发布有两种,一种是正常发布,一种是无异常的发布(即在具体执行事件时关闭了异常处理)。所谓的事件发布是给事件服 务类传递一个事件对象( WFEvent 类),这个事件对象包含了事件类型,线程名,事件 ID ,流程定义 ID ,活动定义 ID ,活动实例 ID ,和工作项 ID 等 等。

  以上简要的描述了事件模型,下面来拿我们平时用的最多的一个构件:结束工作项来详细跟踪它的事件处理。结束工作项可能是最具有代表性的一个流程动作,因为在做这个时间后遍历了整个流程实例的流程:

   1 ,   用户通过引擎的 API 调用 WorkItemManager 类的 finishWorkItem 方 法,该方法通过服务工厂取得持久层的数据访问服务,并根据 workitemID 取得 WFWorkItem 对象。做相关的判断后通过事件工厂类的 createFinishWorkItemEvent 方法创建个事件代码为 3004 的事件对象( WFEvent )。然后通过服务工厂类取得事件服务类把该 事件对象发布给事件处理服务。从此刻就开始了 EOS 事件调度服务的运转。

   2 ,   事件服务类(拿单线程事件服务类做例子)拿到这个事件类后把该事件通过 WFEventDisposer 发布该事件。具体的发布过程很简单,即判断该事件类型是否已注册,如果已经注册则取到改事件代码的注册类。该代码是 3004 ,则应取 WorkItemHandler 。然后调用 WorkItemHandler 的 invoke ()方法,

   3 ,  WorkItemHandler 类 invoke ()中写 到: if(event.getType() == 30004) {finishWorkItem(event);} 则找到该方法,该方法开始做了相关的判断后做相关标志位的修改:置当前工作项的状态为 12 ,然后判断当 前活动是否结束。(大概的算法是取得已经结束的工作项和该活动总的工作项,取得活动定义的多工作项是否启动。如果是多工作项则判断完成个数策略:是按百分 比还是按操作员个数等等,做一系列的判断后得到应该结束的工作项,如果小于等于已经结束的工作项则该活动结束,没有启动多工作项则相应的处理要简单点), 如果该活动已完成,则调用事件服务的结束活动实例事件 createFinishActivityEvent ;如果没有结束则判断工作项启动的策略是 “at_the_same_time” 还是 “one_by_one” ,如果是 “one_by_one” 则找本活动实例下的工作项状态为 1 的工作并启动它。

   4 ,   结束活动实例是调用事件工厂的方法 createFinishActivityEvent ,新建一 个事件代码为 2004 的事件。用 createFinishWorkItemEvent 的方法发布该事件。到 ActivityExecuter 类中找到 finishActivity ,该方法修改活动实例状态为 7 ,填写活动结束时间。如果该活动注册了时限则取消活动时限的注册。如果该活动实例定义了结束活 动的触发动作则触发该动作(通过 WFAppCaller 调用)。最后由事件工厂产生一个事件代码为 1002 的 createScheduleNextActivityEvent 事件。由事件服务发布事件。

   5 ,   启动下个活动实例的事件动作是事件工厂调用 scheduleNextActivity 方法,该方 法通过流程定义找到下个环节的转移条件,并根据转移条件和分支模式(全部分支: AND ;多路分支: XOR ;单一分支: OR )生成一个环节定义列表。引擎首 先把未启动的活动实例和挂起的活动实例找到,如果没有则生成一个活动实例。然后生成一个转移对象( WFTransition ),最后把待启动的活动实例对 象放到一个列表中。根据该列表中的活动定义的启动策略(直接启动,待激活,由规则逻辑指定)来启动活动实例;如果是直接启动活动实例则由事件工厂新建一个 事件代码为 2001 的事件 startActivity ,如果待激活策略则由事件工厂产生事件代码为 2000 的事件 preStartActivity 。同样 如果在流程定义中定义了创建活动实例触发的事件则触发该事件, scheduleNextActivity 方法做了很多业务处理的事情,所以比较复杂。

   6 ,   事件服务调用 startActivity 方法,修改当前活动状态位为 2 ,并向时限管理服务注册时 限,然后通过活动执行类的帮助类分派工作项,分派工作项的过程是判断是否是多工作项,如果不是则按参与人员分派,如果是则判断多工作项的启动策略,启动工 作项业务处理比较复杂,并没有相应的事件代码对应,在这里不详细介绍。
以上的六个步骤完成了我们平时最常用的完成工作项的方法。综上所述应该能够对 EOS 工作流的事件调度机制有个清楚的认识,比如结束工作项的事件调度有 3004->2004->1002->2001 这几种事件的触发。同样还有我们平时比较常用的启动流程实例方法首先是创建一个流程实 例,然后开始事件调度: 10001->10002->2001, 最后是分派工作项。
OSWorkflow 里也有自己的调度机制,但在业务上要比 EOS 简单的多,准确的讲 OSWorkflow 只有两个概念: steps (步骤) 和 actions (动作)。一个简单的调度过程它可能从一个步骤流转到另外一个步骤(或者有时候还是停留在一样的步骤)。它的调度其实就是一个 类: AbstractWorkflow ,这个类里面有两个方法: doAction 和 transitionWorkflow 基本实现了所有的调度(其实也不能算是调度,只能算是状态的迁移)。 OSWorkflow 最大的优点是在执行调度 过程中执行的一系列的 Function (在 SOA 里叫服务模型,在 EOS 里叫展现逻辑),它在执行客户端的服务时的机制时还是比较复杂的,如果感兴趣在工作之余可以看一下。
还有个最近比较流行的开源的引擎, JBpm ,我没看过这个,好象现在又整合到 JBOSS 下去了,好象很复杂。

1.2.  时限管理服务

1.2.1.  时限的分类

http://gocom.primeton.com/uploads/FCKUserFiles/Image/01%2867%29.jpg

  时限类型有两种:一种是一次触发完成时限,还有一种是循环触发(譬如隔多长时间进行一次提醒)并可设置触发的次数。

1.2.2.  时限计算器  

  在工作流引擎启动时就启动一个JVM 唯一实例的时限计算器,该类可以使用引擎默认的。也可以自己去实现一个自定义的计算方法,在配置文件中注册要重写的类名即可。引擎的时限计算器只有两个方法,一个是计算结束时间,还有一个是计算提醒时间。其实是个静态类。

1.2.3.  时限服务的启动

  在引擎中的时限服务有两个,一个是引擎启动的时候启动的时限服务,该服务初始化了时限对象列表;一个是在引擎启动后启动的服务,该服务是对列表 中的时限对象进行轮询,触发超时的时限对象对应的触发事件,并移除该对象时限。时限的线程处理用了大量的过程化程序的结构,在这里还是比较绕人的。

1.2.4.  时限的注册和移除

  在流程引擎中的时限服务其实就是在维护一个时限对象的列表,该列表记载了处于运行状态的活动的时限对象。

  在启动一个环节或启动一个流程时判断该活动或该流程的时限,如果该活动或该流程定义了时限则向时限服务注册该时限;TimerManager 类中的注册方法的实现是调用时限服务类的registeTimer 方法,往时限对象列表(Vector )追加一条记录。

  在结束活动事件时或结束流程时如果是超时的操作则时限对象列表中没有该活动的时限对象,因为该对象已被时限触发器触发并移除。如果没有超时则要 把这个向量列表中的那条时限对象给去掉。在TimerManager 类中的注册移除方法的实现是调用时限服务类的unregisteTimer 方法,往时 限对象列表(Vector )移除一条记录。

1.2.5.  时限事件的触发

  时限的触发完全是后台的线程做的事情。该线程对时限服务所维护的时限对象列表进行轮询,如果发现有超时的对象则触发已定义好的动作,该动作就是我们平时在studio 中设的如果超时则干什么事的触发动作。

  对时限的处理是通过java.util.Timer 这个类来实现的。是通过新建一个时限任务(MyTimerTask )让Timer 来执行。并 向该类传递一个OnceTimerHandler 对象实例。该对象有个方法timerTrigged 就是到了预定时限时触发的方法。该方法首先调用 timerHandler 类的handlerTimer 方法,即如果有触发事件的话就调用上节讨论的事件代码以4 开头的事件。然后修改时限类的当前状态为 3 ,完成一次时限触发动作。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值