工作流调度算法作者:qwerty | 源自:原创 | 发表日期:2007-4-14 | 阅读次数:242 | 【保存文章至硬盘】【打印文章】主要比较:OBE,Shark,OSWorkflow,jBpm。
分析一下他们的调度算法,就基本上可以知道其能力有多强。
OBE 的引擎调度机制说到开源引擎,首先就要说一下OBE,这是最早一款支持XPDL 的开源工作流引擎。
可惜由于没有良好的持续维护,到如今,虽然Adrian 依然还在对其进行一些补充和修改,但已经掩饰不出其“落寞”的容颜了(/james999/category/57982.aspx)。
OBE 的引擎运转调度算法是很简单的,其所有的调度规则都是依据于WorkflowRunner 类的run 方法。
采用遍历循环的方式,这个遍历机制就是:/***** 摘自WorkflowRunner 类的run 方法****/while (!_activityStack.isEmpty()) {//_activityStack 中暂存着需要被激活的活动实例ActivityContext ap = (ActivityContext)_activityStack.pop();_ctx.setActivityContext(ap.activity, ap.instance);//虽然叫execute,但是其实际上是一个激活活动实例的行为executeActivityInstance(ap.activity, ap.instance);}/*在初始化任何一个活动实例后,将这个活动实例放入_activityStack 这个堆栈中。
然后调用WorkflowRunner 的run 方法。
在这个方法中,遍历_activityStack 堆栈中的活动实例,进行运行。
*/但是什么情况下会激活WorkflowRunner 的run 呢?StartProcess,startActivity,completeActivity,executeTransition 这些情况下,都会造成run 的运行。
OBE 的调度算法是很简单,但是执行这个调度过程,是比较绕的。
想弄清楚到底如何运行的,大家有必要去仔细阅读阅读WorkflowRunner 类。
从StartProcess 方法开始,跟踪起startActivity,completeActivity,executeTransition 这几个方法之间的调用关系。
这样的引擎调度机制是比较单一的。
将一些控制判断交给了外围的过程。
引擎本身并没有多少实际的调度,只是一个执行体:获取需要执行(激活)的活动实例,然后执行(激活)。
工作流引擎核心调度算法与PetriNet by 胡长城(银狐999)补充一下,OBE 有个非常值得参考和吸收的地方,就是其Listener 的应用。
虽然Listener对引擎来说只是一个外设,但是却为其跟踪整个引擎得调度留下了很多可扩展接口。
当然OBE 内核主要是两个类:WorkflowRunner(负责引擎调度)和EngineContext (运行环境)。
Shark 的引擎调度机制和OBE 同样支持XPDL 的模型描绘语言的还有一个引擎Shark,Shark 是目前体系结构最为庞大和完善的开源工作流引擎。
不光提供了对分布式的支持(基于Corba),而且提供了多线程的事务安全控制。
Shark 的内部调度机制也比较简单,与OBE 类似。
Shark 的整个调度方法也基本上是基于WfProcessImpl 内的run 方法,也采用的是遍历循环的方式。
只是OBE 是遍历待激活的活动实例,而Shark 是遍历已经完成的活动实例,然后往下推进。
——估计Shark 是故意为了避免。
与OBE 类似,所以选择了这么一种算法。
因为你会发现,他们的执行推进机制是较为相像的。
Shark 遍历循环的机制是:/***** 摘自WfProcessImpl 类的run 方法****/protected void run (SharkTransaction t, WfActivityInternal lastFinishedActivity){//如说是启动流程,启动流程实例的时候,不指定lastFinishedActivityif (lastFinishedActivity==null) {Set starts=getProcessDefinition(t).getStartingActivities();for (Iterator it=starts.iterator(); it.hasNext();) {startActivity(t,asDefId,actDef,null);}}//开始遍历已经结束的活动实例while (lastFinishedActivities.size()>0) {if(!state.equals(SharkConstants.STATE_OPEN_NOT_RUNNING_SUSPENDE D)) {//执行当前活动实例后续的行为queueNext(t, (WfActivityInternal)lastFinishedActivities.get(0));lastFinishedActivities.remove(0);} else {return;}}}/*任何一个活动点完成之后(不论是Complete 还是Terminate),会将自身放入lastFinishedActivities 列表中,然后调用run 方法,促发对这个列表的循环遍历。
*/有兴趣对这方面研究的,可以看看WfProcessImpl 内的start、run、activity_complete、activity_terminate 这几个方法。
工作流引擎核心调度算法与PetriNet by 胡长城(银狐999)从调度机制上说,shark 和obe 基本雷同。
甚至可以看到,其两个运行类都基本上有些类似:shark 是WfProcessImpl 类,obe 是WorkflowRunner 类。
两个类都是即包含了调度的前推因素(比如起动流程实例、活动实例结束等方法),也包含了调度的规则运算(run 方法)。
唯一不同的就是,shark 是对已经完成的活动实例进行遍历,然后前推;而OBE 则是对需要激活的活动实例进行遍历,进行前推。
点评:OBE 和Shark 的run 调度方法,是比较常用的调度机制。
首先效率上比较还是可以,其实比较直观,也容易理解。
而且连个引擎的执行机制有一定的雷同,这可能是由于两者都采用XPDL 的缘故。
但是,受他们调度机制的影响。
OBE 和Shark 是很难支持复杂的运转模型,比如“抢占模式(Workflow Pattern 中叫延迟选择)”。
OSWorkflow 的引擎执行机制OSWorkflow 与其说是一个工作流引擎,不如说是一个“可嵌入式状态机”。
其机制并不类似于我们通常所说的“流程”。
其是以“动作(Action)”作为驱动的。
所以OSWorkflow 用在如“bug 跟踪”等处理流程中,是非常适合的。
从狭义上说,OSWorkflow 是不存在什么调度机制的。
如果硬要说的话,那么就可以从AbstractWorkflow 这个类的两个方法来看:doAction 和transitionWorkflow。
—— OSWorkflow应该说是一个执行机制:执行某一个Action,并将状态从A 转变为B。
这个状态的转变,可能是从一个step 的某一个status 变为另一status;也可能是从一个step 的某一个status 变为另一个step 的某一个status。
(注:对osworkflow 来说,step+status 表现为一个state)。
在这种状态变迁过程中,会执行一系列的Function。
当然,OSWorkflow 有很大的灵活性取决于其Function 机制。
这种Function 机制,有兴趣的可以参考参考。
最近很多人询问我,是否可以将OSWorkflow 用于他们的办公自动化系统中。
其实这方面OSWorkflow 处理起来并不是很适合,通常一个OA 的审批流程是很难用OSWorkflow 做的完美的。
JBpm 的引擎执行机制如果大家对UML 的Activity Diagram 很熟悉的话,理解jBpm 就易如反掌了。
在jBpm中也应用了Token。
但是这个Token 和PetriNet 的概念和语义是不同的。
jBpm 的token 是用于表示“任务分配给某一个actor(执行者,可以是人、系统等等)的依据”,也就是说,只有某一个actor 拿到token,才有可能去执行任务。
——当然,由于jBpm 这一块的算法,也局限了jBpm 的复杂逻辑处理。
从狭义上说,jBpm 这个也不能算是什么调度机制,也只能说是执行机制。
只是这个执行工作流引擎核心调度算法与PetriNet by 胡长城(银狐999)机制要比OSWorkflow 复杂很多。
OSWorkflow 的推进执行机制是Action 的执行,而jBpm 的推进机制则是Token 的转移。
在往下阅读阅读的时候,大家有必要把jBpm 的一些基本元素和理念搞懂。
可以参考:/james999/category/57982.aspx。
首先需要说明的是,jBpm 在将任务分配给某一个Actor(也就是jBpm 所描述得执行者)的时候,也会将一个Token 对象分配给这个Actor。
不论这个Actor 是执行“接受任务”还是“提交任务”,其首选都必须获取一个ExecutionService 对象(当然,每一个Actor 在获取其所需要执行的任务的时候,也就是获取一个token 对象标识,那么这个时候,token 的作用又似乎类似于我们通常所理解的WorkItem 意图)。
/***** jBpm 如何开发调用****/ExecutionService executionService =JbpmServiceFactory.getInstance().openExecutionService(actorId);InvocationLog invocationLog = null;//如果是启动流程实例invocationLog =executionService.startProcessInstance( definitionId,variables,transitionName ) ;//如果是结束任务State(State 是jBpm 的任务点,不要理解成了状态)invocationLog =executionService.endOfState( tokenId,variables,transitionName );当然,看到这儿,大家一定很关心,这个Token 到底是如何产生的?jBpm 引擎会在start of Process Instance 的时候,产生一个root-token。