工作流模式-工作流异常处理模式

在软件开发里,我们将不在自己控制范围内因素所造成的问题和没有预料到的情况称为异 常。工作流异常和软件开发里异常的概念一致,将流程实例执行过程中出现的问题和错误称为异 常,这些异常是由各种不确定因素造成的,从而使流程实例执行偏离了流程设计者最初的期望。 引起工作流异常的因素有很多,流程定义描述的不准确或不完整,执行环境的变化,不能获取资 源等都会引起流程执行偏离预期。这些因素涉及系统异常:硬件、软件、通讯、工作流模型、相 关应用程序、流程逻辑约束、工作流相关数据约束、时间约束以及执行算法;涉及业务异常:工 作人员请假离职、资源紧张、突发事件、用户中止合同、项目目标发生变化等。

在Java和C++里,当程序运行过程中出现异常时,会中止当前程序的运行,并将异常层层抛 出。工作流系统的异常处理与之相同,如果在当前的运行上下文里,我们得不到足够的信息来处 理这个问题,我们就会停止运行,并将这个问题交给上层进行处理,上层拥有更多的信息。在软 件开发里,异常处理的一个原则是:如果不知道该如何处理这个异常,那么就别去捕捉它。这个 原则同样适用于流程实例的执行过程,如果不知道如何处理一个问题,那么就不要处理,把它留 给能处理这个问题的人去处理。

程序里,异常处理的目标在于让我们能用比现在更少的代码,以一种更简单的方式来开发大 型、可靠的程序。通过将处理异常的代码从异常发生的地方移开,我们能够在一个地方集中精力 去解决想解决的问题,然后再到另一个地方集中处理这些异常问题。程序的主线不会被异常处理 这类枝节问题给搞得支离破碎,程序也更易于理解和维护。这一目标同样也是工作流系统异常处 理的目标,当应用工作流系统时,我们首先会进行流程建模,复杂的流程定义会导致流程的不可 管理,作为一个原则,流程定义必须能够被执行工作的人所理解。当对实际业务流程进行建模时, 如果只建模流程执行的乐观路径,模型会直接易懂,但如果将流程执行过程中的各种异常情况和如何处理都进行建模,那么肯定会带来巨大的复杂性,使得流程定义非常难以维护。此时一是需 要工作流系统提供异常处理机制;二是需要应用人员进行适当的异常分类与建模,并在合适的位 置设置异常处理器。

在前面的章节里,我们分别讨论了工作流的控制模式、资源模式和数据模式,这三种模式分 别对工作流的不同方面进行了描述,异常处理模式与这些模式都紧密关联,因为在工作流的执行 中,流程定义、流转、资源分配与数据约束都会引起异常。

在本章里,我们首先对工作流执行过程中可能出现的异常进行分类,一般来说,异常都会与 一个正在执行的工作项关联,即当前正在执行的工作产生了异常,但外部环境的变化也会影响到 当前正在执行的工作。接下来我们将讨论在不同的层次上对异常进行处理,这些层次包括工作项 级别和流程实例级别(包括了流程定义级别),以及异常发生时我们可以采取的恢复动作。当讨 论完这些点之后,我们将会看到它们如何互相结合构成我们的工作流异常处理模式即面。

异常分类

流程执行过程中的异常分为五类,如图D-1所示。

工作流模式-工作流异常处理模式

图D-1 工作流异常分类

  1. 工作项执行失败:工作项所代表的工作不能继续执行或无法按照期望完成。导致工作项 执行失败的原因有很多种,与该工作项相关的硬件故障、软件故障或网络故障都会导致 活动参与者无法正常执行该工作项,同时活动参与者也可能会自己中止该工作项的执行 或者直接放弃。
  2. 超时:工作项未在指定的时间点完成或未在指定的时间点开始执行。
  3. 资源不可用:没有可用的资源执行或完成工作项。有两种情况,一是分配工作项时系统 找不到满足执行该工作条件的资源(人手不够,资源被占用),二是工作项执行过程中,
    先前指定的资源不能继续或无法执行该工作项(生病、离职、调动)。
  4. 外部触发:外部触发通常表现为事件,组织外部的事件影响正在执行中的工作项。例如 用户突然取消订单会导致订单处理流程中所有工作项的中止,并伴随着业务回滚(收回 发货)。
  5. 违反约束:工作流约束包括流程流转的约束(流程死锁,不能继续执行,进入死循环)、 数据的约束、资源的约束(超出当前组织资源所能达到的能力)以及业务约束。工作流 系统运行过程中需要保证流程执行的合理性和一致性,需要对流程执行状态进行持续的 监控。

工作项级别的异常处理

一般来说,异常都会与一个正在执行的工作项关联,即当前正在执行的工作产生了异常,外部触发是个例外,此时异常并不由流程自身执行产生,但是影响到当前正在执行的工作项。当发 生异常时,工作项级别有很多种可能的处理方式,这些方式与当前工作项状态息息相关,所以在 讨论具体的处理方式之前,我们先简单回顾一下工作项的生命周期,然后讨论工作项在其生命周 期的不同状态时可以选择的异常处理方式。

从资源的角度,工作项的生命周期具有6个状态(相比资源模式里的讨论,进行了简化),分 别是:提供给资源拾取(Offered)、指派给一个资源执行(Allocated)、执行(Started)、完成(Completed)、失败(Failed)和撤回(Withdrawn)。如图D-2所示。

工作流模式-工作流异常处理模式

图D-2 工作项的生命周期

最开始,工作流系统创建工作项并将其提供给资源拾取,这里的资源可以是单个资源也可以 是多个资源,工作项此时处于提供给资源拾取状态。接下来,其中一个资源向工作流系统发送一个要求指派的请求,要求系统将该工作项指派给他进行执行,然后系统就会将工作项指派给该资 源执行,工作项此时处于指派给一个资源执行的状态,同时,对于前一步工作项被提供的其他资 源来说,工作项被撤回了。接下来,资源向系统发送开始执行的请求,工作项即进入执行状态。

最后,如果资源正常完成活动,会给系统发送请求,告诉活动完成,系统将工作项的状态置为完 成,工作项的生命周期即到此结束;而如果资源没有按照期望完成活动或无法完成活动,工作项 的状态置为失败,工作项的生命周期结束。

当工作项处于提供给资源拾取的被拾取状态时,可能的异常处理方式包括以下4种。

  1. 继续被拾取(OCO,continue-offer):工作项继续保持被拾取的状态,不发生任何变化。
  2. 重新分配资源拾取(ORO,re-offer):工作项重新分配给新的资源进行拾取。
  3. 强制完成(OFC,force-complete):工作项从资源的待拾取列表撤回,状态变为完成,触发后续活动产生新的工作项。
  4. 强制失败(OFF,force-fail):工作项从资源的待拾取列表撤回,状态变为失败,不触发后续活动。

工作流模式-工作流异常处理模式

图D-3 工作项处于被拾取状态的异常处理

当工作项处于指派给一个资源执行的被指派状态时,可能的异常处理方式包括以下5种。

  1. 继续被指派(ACA,continue-allocation):工作项继续保持被指派的状态,不发生任何变化。
  2. 重新指派(ARA,re-allocation):工作项从资源的待办列表撤回,工作项重新分配给新的 10资源执行。
  3. 重新分配资源拾取(ARO,re-offer):工作项从资源的待办列表撤回,工作项重新分配给新的资源进行拾取。
  4. 强制完成(AFC,force-complete):工作项从资源的待办列表撤回,状态变为完成,触发 后续活动产生新的工作项。
  5. 强制失败(AFF,force-fail):工作项从资源的待办列表撤回,状态变为失败,不触发后 续活动。

工作流模式-工作流异常处理模式

图D-4 工作项处于被指派状态的异常处理

当工作项处于执行状态时,可能的异常处理方式包括以下6种。

工作流模式-工作流异常处理模式

图D-5 工作项处于执行状态的异常处理

  1. 继续执行(SCE,continue-execution):工作项继续保持执行的状态,不发生任何变化。
  2. 重新开始执行(SRS,re-start):当前正在执行的工作中止,由同一个资源重新执行该工作项。
  3. 重新指派(SRA,re-allocation):当前正在执行的工作中止,工作项从资源的办理列表撤回,工作项重新分配给新的资源执行。
  4. 重新分配资源拾取(SRO,re-offer):当前正在执行的工作中止,工作项从资源的办理列表撤回,工作项重新分配给新的资源进行拾取。
  5. 强制完成(SFC,force-complete):当前正在执行的工作中止,工作项从资源的办理列表撤回,状态变为完成,触发后续活动产生新的工作项。
  6. 强制失败(SFF,force-fail):当前正在执行的工作中止,工作项从资源的办理列表撤回,状态变为失败,不触发后续活动。

流程实例级别的异常处理

异常不仅会影响到与其关联的工作,往往还会影响到同一个流程实例里正在执行的其他工作 甚至同一个流程定义里其他正在执行的工作,所以我们还需要在更高的一个级别进行处理。

可能的异常处理方式包括以下3种。

  1. 继续执行(CWC,continue with case):当前流程实例中其他工作不受影响,流程实例继续执行。异常在工作项级别已经得到完全的处理。
  2. 当前流程实例中止执行(RCC,remove current case):当前流程实例中的部分或所有工作中止执行,如果所有工作都中止执行,那么即流程实例中止执行。异常所影响的范围限于当前流程实例内。
  3. 所有属于同一流程定义的流程实例都中止执行(RAC,remove all cases):属于同一流程定义的流程实例中的部分或所有工作中止执行,如果所有工作都中止执行,那么即所有 流程实例都中止执行。异常影响所有属于同一流程定义的流程实例。

恢复动作

当异常发生时,执行过的工作已经产生了一定的影响,为了使流程实例能够继续执行或正常 停止,必须对已执行工作所产生的影响进行消除,这是通过恢复动作完成的,该动作对流程中可 补偿的工作进行补偿。

可能的恢复动作包括以下3种。

  1. 什么都不做(NIL,no action):什么都不做。
  2. 回滚(RBK,rollback):流程实例状态重置至异常发生前的一个回滚点。
  3. 补偿(COM,compensate):额外的补偿动作,消除异常所产生的影响。

对于回滚,我们需要记录一个回滚点,通过执行日志记录流程实例执行过程中的事件,当回滚动作发生时,我们将执行日志中回滚点后续的执行事件全部撤销。我们并不需要将流程实例回 滚后的状态与流程实例当时位于回滚点的状态完全一致,我们需要的只是消除异常所产生的影 响,所以执行日志不需要详细记录流程实例中的每一个执行事件。同时,有一些操作也是回滚动 作无法恢复的,例如资源浪费的时间、已经发送的文档/报告等。

补偿动作相对回滚来说有很多的灵活性,我们不仅可以将发生过的事件撤回,还可以额外进 行一些操作(例如通过异常路径继续执行流程实例),来消除异常所带来的影响。

工作流模式-工作流异常处理模式

图D-6 BPMN里的异常流属于补偿动作

异常处理模式

对每一种的异常而言,都可能存在不同的处理方式,我们把每种针对特定异常类型可能的处 理方式称为异常处理模式。一个异常处理模式包括4方面的内容:

  1. 异常的类型;
  2. 发生异常的活动如何处理即工作项级别对异常的处理;
  3. 发生异常的流程实例以及关联流程实例如何处理即流程实例级别对异常的处理; 1. 采取何种恢复动作。

共有135种可能的异常处理模式。

工作执行失败

  1. OFF-CWC-NIL
  2. OFF-CWC-COM
  3. OFC-CWC-NIL
  4. OFC-CWC-COM
  5. AFF-CWC-NIL
  6. AFF-CWC-COM
  7. AFC-CWC-NIL
  8. AFC-CWC-COM
  9. SRS-CWS-NIL
  10. SRS-CWC-COM
  11. SRS-CWC-RBK
  12. SFF-CWC-NIL
  13. SFF-CWC-COM
  14. SFF-CWC-RBK
  15. SFF-RCC-NIL
  16. SFF-RCC-COM
  17. SFF-RCC-RBK
  18. SFC-CWC-NIL
  19. SFC-CWC-COM
  20. SFC-CWC-RBK

工作超时

  1. OCO-CWC-NIL
  2. ORO-CWC-NIL
  3. OFF-CWC-NIL
  4. OFF-RCC-NIL
  5. OFC-CWC-NIL
  6. ACA-CWC-NIL
  7. ARA-CWC-NIL
  8. ARO-CWC-NIL
  9. AFF-CWC-NIL
  10. AFF-RCC-NIL
  11. AFC-CWC-NIL
  12. SCE-CWC-NIL
  13. SCE-CWC-COM
  14. SRS-CWC-NIL
  15. SRS-CWC-COM
  16. SRS-CWC-RBK
  17. SRA-CWC-NIL
  18. SRA-CWC-COM
  19. SRA-CWC-RBK
  20. SRO-CWC-NIL
  21. SRO-CWC-COM
  22. SRO-CWC-RBK
  23. SFF-CWC-NIL
  24. SFF-CWC-COM
  25. SFF-CWG-RBK
  26. SFF-RCC-NIL
  27. SFF-RCC-COM
  28. SFF-RCC-RBK
  29. SFC-CWC-NIL
  30. SFC-CWC-COM

资源不可用

  1. ORO-CWC-NIL
  2. OFF-CWC-NIL
  3. OFF-RCC-NIL
  4. OFC-CWC-NIL
  5. ARO-CWC-NIL
  6. ARA-CWC-NIL
  7. AFF-CWC-NIL
  8. AFF-RCC-N1L
  9. AFC-CWC-NIL
  10. SRA-CWC-NIL
  11. SRA-CWC-COM
  12. SRA-CWC-RBK
  13. SRO-CWC-NIL
  14. SRO-CWC-COM
  15. SRO-CWC-RBK
  16. SFF-CWC-NIL
  17. SFF-CWC-COM
  18. SFF-CWC-RBK
  19. SFF-BCC-NIL
  20. SFF-BCC-COM
  21. SFF-RCC-RBK
  22. SFF-RAC-NIL
  23. SFC-CWC-NIL
  24. SFC-CWC-COM

外部触发

  1. OCO-CWC-NIL
  2. OFF-CWC-NIL
  3. OFF-RCC-NIL
  4. OFC-CWC-NIL
  5. ACA-CWC-NIL
  6. AFF-CWC-NIL
  7. AFF-RCC-NIL
  8. AFC-CWC-NIL
  9. SCE-CWC-NIL
  10. SRS-CWC-NIL
  11. SRS-CWC-COM
  12. SRS-CWC-RBK
  13. SFF-CWC-NIL
  14. SFF-CWC-COM
  15. SFF-CWC-RBK
  16. SFF-RCC-NIL
  17. SFF-RCC-COM
  18. SFF-RCC-RBK
  19. SFF-RAC-NIL
  20. SFC-CWC-NIL
  21. SFC-CWC-COM

违反约束

  1. SCE-CWC-NIL
  2. SRS-CWC-NIL
  3. SRS-CWC-COM
  4. SRS-CWC-RBK
  5. SFF-CWC-NIL
  6. SFF-CWC-COM
  7. SFF-CWC-RBK
  8. SFF-RCC-NIL
  9. SFF-RCC-COM
  10. SFF-RCC-RBK
  11. SFF-RAC-NIL
  12. SFC-CWC-NIL
  13. SFC-CWC-COM

其他的异常分类和处理

Bruce Silver 的异常处理模式

Bruce Silver(http://brsilver.com/)的异常处理基于BPMN,关注如何建模以显式的捕获异常, 异常通过异常路径进行处理。这种定义存在的局限:异常处理过于单一,例如,很多时候我们并 不需要定义异常路径,我们需要做得仅仅是暂停流程实例并通知流程实例负责人;并未涉及异常 处理的细节,例如,对工作项的各种可能异常处理都没有涉及;所捕获的异常属于可预知的异常(所以可以进行建模),对不可预知异常没有涉及。

Bruce Silver根据异常产生的原因进行了分类,如图D-7所示。

  1. 业务异常:工作项能够执行完成,但是返回异常的结果(排除系统异常)。根据异常产生 的原因又分为组织内部原因触发和组织外部原因触发。内部触发包括了活动参与者做出 的非期望行为(取消活动)、超时、业务规则约束(资源不可用),外部触发包括了用户 突然取消或改变决定。
  2. 系统异常:与工作项相关的硬件故障、软件故障或网络故障所导致的工作项无法执行。例如工作项调用的Web服务连接超时、调用失败。

工作流模式-工作流异常处理模式

图D-7 根据原因所进行的异常分类

Bruce Silver将异常处理模式分为了7种:内部业务异常处理、重新抛出异常处理、活动超时处理、系统异常处理、非请求的外部异常处理、请求等待响应的外部异常处理和事务。

内部业务异常处理

组织内部原因导致的业务异常,包括活动参与者做出的非期望行为和违反业务规则约束,例 如活动参与者对订单审批不通过、业务数据不完整、送货前发现缺货。此时使用网关进行建模, 不使用异常事件。

工作流模式-工作流异常处理模式

图D-8 内部业务异常处理

重新抛出异常处理

下层流程无法完全处理的异常继续抛出,上层流程继续处理。

工作流模式-工作流异常处理模式

图D-9 抛出/捕获异常

活动超时处理

活动处理超时。

工作流模式-工作流异常处理模式

图D-10 活动超时处理

系统异常处理

调用的Web服务连接超时、调用失败,与人无关。

工作流模式-工作流异常处理模式

图D-11 系统异常处理

非请求的外部异常处理

非请求的外部触发意味着流程实例执行过程并未向外部请求响应,事件是由外部驱动触发 的。使用消息事件捕获外部触发,将外部触发可能影响的活动建模为块活动或者子流程(建立起 上下文),忽略过早或过晚的外部触发。

工作流模式-工作流异常处理模式

图D-12 BPMN建模的外部触发异常处理

请求等待响应的外部异常处理

请求等待响应意味着流程实例执行过程中需要与外部交互,向外部发送请求并等待外部的响 应。使用事件网关进行建模。

工作流模式-工作流异常处理模式

图D-13 请求外部响应异常处理

事务

事务由事务协议所支持,如 WS-Transaction。

Ziv Ben-Eliahu 和 Michael Elhadad 的语义流程建模

Ziv Ben-Eliahu和Michael Elhadad的论文(http://www.cs.bgu.ac.il/~bpmn/wiki.files/Paper.pdf) 从Java的Checked异常获取灵感,扩展了BPMN的活动,给活动增加了3种元数据:语义属性、异 常和异常处理器。异常定义活动运行期可能产生的异常(异常事件),异常处理器定义活动对应 的异常处理器,语义属性对活动进行语义描述。

实际应用时,业务人员快速对乐观路径进行建模,定义活动时输入可能产生的异常然后迅速 跳过。稍后,建模工具对活动定义进行检查(类似于Java里的编译检查),如果找不出活动异常 对应的处理器,那么报错,强制业务分析人员返回来继续异常处理的建模。如果业务分析人员没 有输入异常,那么建模工具会根据活动具有业务语义的关键词或活动定义本身推断出活动运行期 可能出现的异常,依据推断异常进行检查。例如,一个通知顾客的活动,语义关键词是通知,那 么工具推断出通知包括了3步:发送数据、接受返回数据和对返回数据进行处理,那么可能的异常就包括:发送数据失败、返回数据超时和返回数据非法。基于活动语义的异常推断需要我们建 立起相应的语义仓库,实现关键词的检索和推断。

Ziv Ben-Eliahu和Michael Elhadad强制业务分析人员对异常进行建模处理的原因在于:他们认 为对于复杂流程来说,业务分析人员往往关注于乐观路径而忘记对异常进行处理;存在不熟悉业 务的建模人员,这些人需要从语义推断中获得建议。

我们认为作者还有一层考虑,即认为捕获异常的最佳时机是在建模的时候,也就是在流程执 行之前,这和Java的基本哲学一致:糟糕的代码根本就得不到执行。

但Ziv Ben-Eliahu和Michael Elhadad的异常处理忽略了两种情况:一是对所有异常都建模处理 会增加流程模型的复杂性,这违反了异常处理的目标:保持流程模型的简单、所有执行者都可理 解;二是作为一个通用的实践:大部分流程执行过程中的异常处理都是脱离于工作流系统之外的, 是由人来处理的,工作流系统只是通知。工具只是工具。

上一篇:[Leetcode]17.二叉树中和为某一值的路径


下一篇:Leetcode61. 旋转链表