BT

如何利用碎片时间提升技术认知与能力? 点击获取答案

《流程的永恒之道》(一):控制模式之串行、并发分裂及并发汇聚模式

| 作者 辛鹏 荣浩 关注 0 他的粉丝 发布于 2014年5月1日. 估计阅读时间: 39 分钟 | 都知道硅谷人工智能做的好,你知道 硅谷的运维技术 也值得参考吗?QCon上海带你探索其中的奥义

控制模式是流程的中枢神经,它在作战小分队中负责将多个单独的作战活动组合在一起,并推动活动的自动化流转,形成作战流程。其重要性不言而喻,因此要设计一个好的流程,就必须学会应用各种各样的控制模式。

在探寻每个模式的究竟之前,我们首先定义一个统一的格式,对于控制模式,将按照如下统一的格式进行描述:

模式描述

我们在探寻每个控制模式时,将按照如下统一的格式进行描述。

  • 原型实例(故事片段)

    给出此模式的故事片段,通过鲜活的工作流故事展现此模式的应用场景。

  • 上下文(描述、动机)

    给出此模式的具体描述和动机:为什么有此模式,是为了解决什么问题。

  • 问题的本质

    此模式的本质是什么?即本质上要做什么事情?

  • 解决方案及技术实现

    给出此模式的解决方案及技术实现。

  • 约束及可能存在的问题

    此模式可能存在的约束和问题。模式并不是万能的,在软件中没有银弹,同样也没有包治百病的模式,每种模式都有可能存在一些约束及限制条件。应用此模式可能会引发什么问题,怎样解决这些问题。

  • 规范中的实现

    给出此模式在相关规范中的实现。目前流程有三大规范XPDL、BPEL、BPMN,我们将按照每个规范的最新版本XPDL 2.1(需要说明的是,本章中的XPDL示例,都是由BizAgi Process Modeler 2.1.0.1生成的)和BPMN 2.0(所有BPMN 2.0的XML定义,都是由signavio提供的在线流程建模器生成的),来描述当前模式在其中的实现。对于BPEL,我们始终认为它不是一个“流程”语言,其本质上是一个Web服务的编制语言,因此只有部分模式使用BPEL描述。

  • 与其他模式的关系

    此模式与其他模式有什么样的关系?是否有配对使用的要求?是否有与其他模式进行组合,解决复杂场景的情形?

1.1.1 房改购房审批流程中的串行模式

图3.2是江南市房管局房改购房立等可取的审批流程。在这个流程中,所有的作战活动都是串行在一起的,完成一个活动才能操作下一个活动,这就是工作流控制模式中的“串行模式”。串行模式极其简单,这里就不按照统一格式进行描述了。

图3.2 房改购房立等可取的审批流程

1.1.2 房改购房审批流程中的“并发分裂“与”并发汇聚“模式

1. 并发分裂模式

  • 原型实例(故事片段)

图3.3 房改购房审批流程的“并发分裂”原型实例

如图3.3所示,复杂的房改购房流程需要两个核查岗位进行核查,因此在“复审”环节之后,并发分裂为了两个活动:“查封核查一”与“查封核查二”。在这个故事片段中,为了提高效率,两个核查环节并行工作,从而将核查的时间缩短了一半。

  • 上下文(描述、动机)

描述:并发分裂,就是在某个活动(本例是“复审”)之后,并发地分裂出多个活动(“查封核查一”、“查封核查二”),这多个活动同时执行。

动机:通过增加流程中并行处理的活动的个数,缩短串行时间,提高整个流程的效率。在1.2.2节中,我们曾经提到过并行工程,并发模式是一种最有效的提高流程效率的模式。

  • 问题的本质

并发分裂模式的动机是尽量增加流程中可以并行处理的活动的个数,从而极大提高流程的效率。那么,怎样才能提高并行活动的数量呢?或者说,并行活动的增加依赖于什么呢?答案就是资源,即执行活动本身所需要的相关资源(3.4节会重点讲述这一主题)。资源如果不够用就没法并行,在房改购房审批流程中,如果只有一个核查岗位当然就无法并行了,因为一个岗位不能同时做两件事情。本例设置了两个查封核查的岗位,因此可以设置为并行活动。

因此,活动能不能并行处理,就要看这个活动本身的相关资源能否并行。再如,在非计算机化的人工流程中,如请假流程,如果请假人拿一张纸质的请假单去找领导签字,那么他只能按照串行顺序去找所有的领导签字,因为物理存在的纸只有一张。而计算机的一个很大作用就是把纸质的数据电子化了,电子化的本质是电子数据本身可以被同步处理,这就是说在同一个电子请假单上,多位领导可以同时签字。因此,原来由于资源的限制而不能并行处理的活动,在计算机化之后可以并行处理了。

  • 解决方案及技术实现

解决方案。要实现并发分裂模式,可以在要并发的活动之后添加一个标识并发分裂的路由节点,如图3.4中的AndSplit,被称为显式的实现方案。或者省略路由节点,直接在活动之后连接两路并发的活动(参见图3.5),被称为隐式的实现方案。

图3.4 并发分裂模式之显式实现方案

图3.5 并发分裂模式之隐式实现方案

技术实现。在工作流中实现并发分裂模式,技术实现包括两部分:一部分是在流程定义期,一部分是在流程运行期。

(1) 定义期。通过可视化的流程设计器,采用此模式的两种方案中的任意一种,画出流程定义图,并持久化存储到数据库。持久化存储的流程定义,在运行期又逆向解析为多个工作流对象之间的关系,如图3.6和图3.7所示的结构。

图3.6 活动与转移的类图

(2) 运行期。流程引擎需要读取持久化存储的流程定义(一般为XML格式),并解析为对象及对象集合之间的关系(活动集合Activities、转移集合Transitions),如图3-7所示。

图3.7 并发分裂模式中隐式方案的对象关系图

可以看出,这是没有采用AndSplit网关的隐式实现方案:

(1) 将XML的流程定义解析为对象之间的关系,本质上就是ActivityInfo与inComingTransitions、outgoingTransitions的关系。inComingTransitions集合存储进入当前活动的转移线对象,outgoingTransitions集合存储离开当前活动的转移线对象。当某个活动实例(例如“活动A”的实例)完成需要转出时,则首先取得“活动A”这个ActivityInfo对象的outgoingTransitions集合并进行迭代,并对每个transitionInfo对象上的condition表达式(用来判断当前转移要执行的条件)进行求值判断,如果判断结果为true,则执行此转移。

(2) 对于并发分裂模式,如果其转移线上的条件表达式condition不设置,即永远默认为true,那么在并发分裂模式中,其所有的后继转移线(outgoingTransitions集合)都将被执行,从而实现并发分裂模式。

(3) 每个outgoingTransition对象(TransitionInfo)执行完毕后,则根据TransitionInfo对象的toActivityId属性取得此ID对应的活动对象(ActivityInfo),在本模式中即“活动B”与“活动C”两个对象,在每次迭代中,对ActivityInfo对象进行实例化,并持久存储,即完成了一次迭代。所有迭代执行完毕,即实现了“活动A”并发分裂为“活动B”与“活动C”的运行期功能。

注:后续所有控制模式的运行期实现,都将遵循outgoingTransitons、incomingTransitions、fromActivityId、toActivityId、condition这样的机制,因此在后续的控制模式中,不再单独讲述这一部分内容了。

对于更细层次的编码实现,目前有基于petri网的Token机制(例如jBPM),也有基于实例的状态机方式。

  • 约束及可能存在的问题

驳回的问题。在本模式的描述、解决方案及实现中,我们可以看到并发分裂模式的AndSplit网关之后分裂出的所有分支都是同时执行的,因此如果驳回到AndSplit网关之前的活动(例如由“活动B”驳回到“活动A”,而“活动C”并不驳回),再次执行到AndSplit网关时,“活动B”重新执行了一次,而“活动C”也会再次被执行,但是“活动C”并没有被驳回,它还需要不需要被再次执行呢?在并发分裂模式中,“活动C”必须再次被执行。但是这可能不是业务所期望的结果(业务上可能会要求“活动C”不要再重复执行了),遗憾的是并发分裂模式解决不了这个问题。目前很多的工作流产品,虽然支持并发分裂模式,但是对于并发分裂的驳回只能做到“活动B”与“活动C”全部重复执行。如果要支持可控制“活动C”是否重复执行,只能引入多选分裂模式与多选汇聚模式两者结合来实现(详见10.3.2.2节的的多选分裂模式与10.3.2.3节的多选汇聚模式)。

  • 规范中的实现

XPDL 2.1的实现(此XML内容由BizAgi Process Modeler直接导出,去掉了与控制模式无关的属性)

<WorkflowProcess Id="4df39a5e-9a54-490c-aca7-7cf45bf53ef0" Name="Process 1">
  <Activities>
    <Activity Id="a2dca2db-f0ab-4c1d-92c4-2f51d356f51e" Name="活动A">
      <Description />
      <Implementation>
        <Task />
      </Implementation>
</Activity>
<Activity Id="67e100a6-f17c-4fb9-80da-c987d918c747" Name="并发分裂网关">
      <Description />
      <Route GatewayType="AND" />
    </Activity>
    <Activity Id="76f2bf26-f953-40b1-800b-25af4320dbd4" Name="活动B">
      <Description />
      <Implementation>
        <Task />
      </Implementation>
    </Activity>
    <Activity Id="ba088ccb-d74f-409b-8d6c-08c7cf577f70" Name="活动C">
      <Description />
      <Implementation>
        <Task />
      </Implementation>
    </Activity>
  </Activities>
  <Transitions>
    <Transition Id="344afd1f-90d9-4d4a-97a2-9b6d909b8601" From="a2dca2db-f0ab-4c1d-92c4-2f51d356f51e" To="67e100a6-f17c-4fb9-80da-c987d918c747" Name="">
      <Condition />
    </Transition>
    <Transition Id="b0b4d8d6-c86d-4262-afda-c6e03925c1d7" From="67e100a6-f17c-4fb9-80da-c987d918c747" To="76f2bf26-f953-40b1-800b-25af4320dbd4" Name="">
      <Condition />
    </Transition>
    <Transition Id="ed12d3a8-7ff4-4cd0-a19c-6eb89fbe5a56" From="67e100a6-f17c-4fb9-80da-c987d918c747" To="ba088ccb-d74f-409b-8d6c-08c7cf577f70" Name="">
      <Condition />
    </Transition>
  </Transitions>
  <ExtendedAttributes />
</WorkflowProcess>

如上所示,XPDL规范采用的显式方案(参见图3.4)来实现并发分裂模式。通过并发分裂网关(<Activity Id="67e100a6-f17c-4fb9-80da-c987d918c747" Name="并发分裂网关">)与转移(Transition Id=" b0b4d8d6-c86d-4262-afda-c6e03925c1d7"Transition Id=" ed12d3a8-7ff4-4cd0-a19c-6eb89fbe5a56")直接建立关系,实现并发分裂网关与分裂转移线的关联。再通过转移线上的From与To属性(见粗体部分),分别指向某个活动Id,实现了线与活动的连接。

BPEL中的实现(XML内容由Eclipse BPEL Designer 生成,去掉了与控制模式无关的属性)

在BPEL规范中,通过<flow>活动提供并发分裂模式的实现。把需要并发执行的活动放置在<flow>…</flow>标签之内。

<bpel:process name="ParallelPattern"
         targetNamespace="http://sample.bpel.org/bpel/sample"
         suppressJoinFailure="yes"
         xmlns:tns="http://sample.bpel.org/bpel/sample"
         xmlns:bpel="http://docs.oasis-open.org/wsbpel/2.0/process/executable"
         >
	<bpel:sequence name="main">
  	<bpel:empty name="Empty"></bpel:empty>
    	<bpel:flow name="Flow">
        	<bpel:invoke name="Invoke1"></bpel:invoke>
        		<bpel:invoke name="Invoke2"></bpel:invoke>
      	</bpel:flow>
    	<bpel:invoke name="Invoke"></bpel:invoke>
  	</bpel:sequence>
</bpel:process>

BPMN 2.0中的实现(XML内容由Signavio Process Editor导出,去掉了与控制模式无关的属性

<process id="sid-a0718229-83f5-4ab3-9fea-63676de90575" isExecutable="false">
    <task completionQuantity="1" id="sid-81CDFBD9-1D50-42CF-B67A-674591D44F4D" isForCompensation="false" name="活动A" startQuantity="1">
       <outgoing>sid-FE93BF67-3DE5-4DF9-BAA7-A7F4BD8C3339</outgoing>
    </task>
    <parallelGateway gatewayDirection="Diverging" id="sid-681399DD-E962-4174-85DA-43384A9B0DD8" name="并发分裂网关">
       <incoming>sid-FE93BF67-3DE5-4DF9-BAA7-A7F4BD8C3339</incoming>
       <outgoing>sid-91DD542C-DBA5-49E0-9E56-A31DA8E4E432</outgoing>
       <outgoing>sid-9DEA8CF3-6EBE-4EB5-B649-42865D58C35D</outgoing>
    </parallelGateway>
    <task completionQuantity="1" id="sid-9FCD307D-BDED-404F-988B-100311E9C92A" isForCompensation="false" name="活动B" startQuantity="1">
       <incoming>sid-91DD542C-DBA5-49E0-9E56-A31DA8E4E432</incoming>
    </task>
    <task completionQuantity="1" id="sid-65D2670A-F5CF-4721-854B-F1499C0F3239" isForCompensation="false" name="活动C" startQuantity="1">
       <incoming>sid-9DEA8CF3-6EBE-4EB5-B649-42865D58C35D</incoming>
    </task>
    <sequenceFlow id="sid-FE93BF67-3DE5-4DF9-BAA7-A7F4BD8C3339" name="" sourceRef="sid-81CDFBD9-1D50-42CF-B67A-674591D44F4D" targetRef="sid-681399DD-E962-4174-85DA-43384A9B0DD8"/>
    <sequenceFlow id="sid-91DD542C-DBA5-49E0-9E56-A31DA8E4E432" name="" sourceRef="sid-681399DD-E962-4174-85DA-43384A9B0DD8" targetRef="sid-9FCD307D-BDED-404F-988B-100311E9C92A"/>
    <sequenceFlow id="sid-9DEA8CF3-6EBE-4EB5-B649-42865D58C35D" name="" sourceRef="sid-681399DD-E962-4174-85DA-43384A9B0DD8" targetRef="sid-65D2670A-F5CF-4721-854B-F1499C0F3239"/>
 </process>

如上所示,BPMN2.0规范同样采用了显式的实现方案。需要注意的是,在XPDL 2.1规范中,通过在Transition的From与To属性实现了线对活动的连接,而在BPMN 2.0规范中,则是通过sequenceFlow的sourceReftargetRef属性(见粗体部分)实现线对活动的连接。

  • 与其他模式的关系

(1) 与会签模式的区别。此处需要注意的是,活动B和活动C是两个不同的活动(也就是说两个活动做不同的事情,例如在办公用品采购预算审批的流程中,活动A为总经理审批,审批通过后,活动B为通知采购员领款,活动C为从财务系统中扣款),因此要区别于同一个活动有两个办理人同时办理(即单步会签)的情况。

(2) 与并发汇聚模式(同步模式)的关系。并发分裂之后,一般都会在某个活动上进行汇聚。此时就需要用到“并发汇聚模式”。一般情况下,并发分裂模式与并发汇聚模式都是成对出现。当然这并不是必须的,因为并发分裂也可以不进行汇聚,例如传阅模式(异步多实例模式)。

(3) 与单选分裂(排他选择)模式组合使用,间接实现多选分裂模式

图3.8 并发分裂模式与单选分裂(排他选择)模式的结合使用

从图3.8中可以看出,通过将并发分裂模式与排他选择模式相结合,可以部分地实现M选N分裂模式。图中实现的效果是,A分裂为BCD或BCE。在一定程度上实现了4选3的效果。当然活动B和活动C是必选的,活动D和活动E则是排他选择(即二选一)。所以,这与多选分裂又有一定的区别,在多选分裂模式中,选择结果可以是任意一个分支,并没有任何限制。

2.并发汇聚模式(同步模式)

  • 原型实例(故事片段)

 

图3.9 房改购房审批流程之“并发汇聚”片段图

如图3.9所示,在“查封核查一”和“查封核查二”两个活动之后,房改购房审批流程通过一个“并发汇聚”网关,继续向下流转到“制证”活动上。

  • 上下文(描述、动机)

描述:并发分裂出的多个活动(“查封核查一”和“查封核查二”)全部执行完毕后,后续活动才会被触发。

动机:为通过并发分裂模式创建出的分支提供一种再次汇聚的机制。

  • 问题的本质

当活动涉及的资源需要合并处理时,则采用同步机制。这里的资源包括某些成果的合并(例如,两个核查结果做合并)。

  • 解决方案及技术实现

解决方案。由于此模式一般都与并发分裂模式配对使用。对应于并发分裂模式中的显式方案和隐式方案,此模式的解决方案同样分为显式方案与隐式方案,如图3.10和图3.11所示。

图3.10 同步模式的显式实现方案

图3.11 并发汇聚(同步)模式的隐式实现方案

技术实现。在此模式中,显式方案与隐式方案的实现稍有不同。

(1) 定义期:对于显式方案(如图3.10),直接在设计器中提供AndJoin活动节点即可,不需要另外设置。而对于隐式方案(如图3.11),由于同步模式要求所有参与汇聚的节点必须全部完成才能触发后续分支,因此必须在“任务D”上设置同步等待的标志(或者称之为“与汇聚”标志)。

(2) 运行期:如图3.12所示,对于“活动D”这个对象,其属性incomingTransitions是一个集合,集合中包含id=”tran_3”和id=”tran_4”的两个转移对象,当两个转移对象都执行完毕时,触发活动D的执行,即可实现同步模式。

图3.12 并发汇聚(同步)模式的隐式方案对象关系图

  • 约束及可能存在的问题

与并发分裂模式中的驳回相对应,此模式同样存在驳回问题:如果并发需要驳回,则必须采用多选汇聚模式来代替同步模式,才能满足驳回需求。

  • 规范中的实现

XPDL 2.1中的实现

<WorkflowProcess Id="b8043656-a95c-40e4-917e-2919a59fbfb3" Name="Process 1">
  <Activities>
    <Activity Id="1c38df40-d035-4155-b473-d81fa606c2a8" Name="活动B">
      <Description />
      <Implementation>
        <Task />
      </Implementation>
    </Activity>
    <Activity Id="d666bc95-edda-446f-9f93-98f27c53a7f8" Name="活动C">
      <Description />
      <Implementation>
        <Task />
      </Implementation>
    </Activity>
    <Activity Id="b2fc7f93-aca6-4d52-965a-432553f02c9c" Name="并发汇聚网关">
      <Description />
      <Route GatewayType="AND" />
    </Activity>
    <Activity Id="194e2c59-9218-49d5-baf4-e9feb715d9f0" Name="活动D">
      <Description />
      <Implementation>
        <Task />
      </Implementation>
    </Activity>
  </Activities>
  <Transitions>
    <Transition Id="a6a47f65-cc0d-4302-870a-5f43556be0e4" From="1c38df40-d035-4155-b473-d81fa606c2a8" To="b2fc7f93-aca6-4d52-965a-432553f02c9c" Name="">
      <Condition />
    </Transition>
    <Transition Id="fe10657f-542a-4229-be3a-382b4aa2a7ad" From="d666bc95-edda-446f-9f93-98f27c53a7f8" To="b2fc7f93-aca6-4d52-965a-432553f02c9c" Name="">
      <Condition />
    </Transition>
    <Transition Id="735397b6-fb5f-4712-a944-59760458403c" From="b2fc7f93-aca6-4d52-965a-432553f02c9c" To="194e2c59-9218-49d5-baf4-e9feb715d9f0" Name="">
      <Condition />
    </Transition>
  </Transitions>
  <ExtendedAttributes />
</WorkflowProcess>

如上所示,在XPDL规范中,采用的是显式同步方案。

BPEL 2.0中的实现(XML内容由Eclipse BPEL Designer 生成,去掉了与控制模式无关的属性)

同样采用<flow></flow>标签进行实现。

<bpel:process name="ParallelPattern"
         targetNamespace="http://sample.bpel.org/bpel/sample"
         suppressJoinFailure="yes"
         xmlns:tns="http://sample.bpel.org/bpel/sample"
         xmlns:bpel="http://docs.oasis-open.org/wsbpel/2.0/process/executable"
         >
	<bpel:sequence name="main">
  	<bpel:empty name="Empty"></bpel:empty>
    	<bpel:flow name="Flow">
        	<bpel:invoke name="Invoke1"></bpel:invoke>
        		<bpel:invoke name="Invoke2"></bpel:invoke>
      	</bpel:flow>
    	<bpel:invoke name="Invoke"></bpel:invoke>
  	</bpel:sequence>
</bpel:process>

BPMN 2.0中的实现(XML内容由Signavio Process Editor导出,去掉了与控制模式无关的属性

<process id="sid-921359f3-d331-4fa7-aad9-7a6eb6e32d4a" isExecutable="false">
    <task completionQuantity="1" id="sid-4038BF10-88DD-4CD4-B51E-2A56B30D372F" isForCompensation="false" name="活动B" startQuantity="1">
       <outgoing>sid-C3C65849-94FE-4D2A-B5F9-500BF6F01C6A</outgoing>
    </task>
    <parallelGateway gatewayDirection="Converging" id="sid-AA7B31AA-C305-4B94-B48B-BA695B497FBD" name="并发汇聚网关">
       <incoming>sid-C3C65849-94FE-4D2A-B5F9-500BF6F01C6A</incoming>
       <incoming>sid-D2F19E17-1F1E-4234-AF1C-D5F85D99AE2D</incoming>
       <outgoing>sid-8275F97F-34DB-4E4F-B73B-72FA44CAE1F5</outgoing>
    </parallelGateway>
    <task completionQuantity="1" id="sid-7E91DCFB-7845-48A6-8263-842B46D0744C" isForCompensation="false" name="活动C" startQuantity="1">
       <outgoing>sid-D2F19E17-1F1E-4234-AF1C-D5F85D99AE2D</outgoing>
    </task>
    <task completionQuantity="1" id="sid-225CB518-1A4E-46A5-80D1-DAF2143B5233" isForCompensation="false" name="活动D" startQuantity="1">
       <incoming>sid-8275F97F-34DB-4E4F-B73B-72FA44CAE1F5</incoming>
    </task>
    <sequenceFlow id="sid-C3C65849-94FE-4D2A-B5F9-500BF6F01C6A" name="" sourceRef="sid-4038BF10-88DD-4CD4-B51E-2A56B30D372F" targetRef="sid-AA7B31AA-C305-4B94-B48B-BA695B497FBD"/>
    <sequenceFlow id="sid-D2F19E17-1F1E-4234-AF1C-D5F85D99AE2D" name="" sourceRef="sid-7E91DCFB-7845-48A6-8263-842B46D0744C" targetRef="sid-AA7B31AA-C305-4B94-B48B-BA695B497FBD"/>
    <sequenceFlow id="sid-8275F97F-34DB-4E4F-B73B-72FA44CAE1F5" name="" sourceRef="sid-AA7B31AA-C305-4B94-B48B-BA695B497FBD" targetRef="sid-225CB518-1A4E-46A5-80D1-DAF2143B5233"/>
 </process>

如上所示,BPMN规范同样采用的是显式同步模式方案。

  • 与其他模式的关系

一般与并发分裂模式配对使用。


感谢张龙对本文的审校,感谢张龙对本文的审核。

给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ)或者腾讯微博(@InfoQ)关注我们,并与我们的编辑和其他读者朋友交流。

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

本书互动网的预售地址 by 辛 鹏

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

1 讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT