前几天我在论坛中看到有人提出一个问题,他问到当工作流中的某个活动生成多值数据时,工作流中的其他活动应如何执行。换句话说,我的其中一个活动返回一系列内容。接下来的活动如何反应呢?它们是将并行运行还是按顺序运行?与许多事情一样,答案是不一定,它取决于您如何组织 Runbook 中的活动。

让我们先从最简单的情况着手。我有一个“运行 .Net 脚本”活动,它运行以下命令:

$collection = gci C:\ | Select Name

它随后将变量 Collection 发布到数据总线以便后续活动可以访问相应数据。此活动运行时,它返回文件和目录名称列表,该列表是以多值数据(数据项集合或列表)形式返回的。在最简单的情形中,“运行 .Net 脚本”活动后面只有一个活动。我将使用“发送事件日志消息”活动,因为如果我是写入文件且活动以并行方式运行,我将不必处理任何文件锁定问题。

现在,我们假定该脚本返回一个包含 10 项的列表。这会使下一个活动运行 10 次,但它是按顺序运行还是并行运行呢?答案是它们按顺序运行。

在单个 Runbook 中,您可以将此流程想象成在不同活动之间传递令牌。一般来讲,一个活动一次只能处理一个令牌。某些活动接受一个令牌并生成多个令牌,例如在上面的示例中,“初始化数据”活动将一个令牌发送到“运行 .Net 脚本”活动,后者根据该脚本的结果生成 10 个令牌(或者,如果您选中了“平展”设置,它可以只生成 1 个令牌)。但是,它不能同时将所有 10 个令牌传递给下一个活动,而是必须一次传递一个。当下一个活动将此令牌传递给随后的活动(或者它“到达 Runbook 的末尾”)时,它可以接受另一个令牌。

那么,如果您在生成多个值的活动之后有更多活动,会怎样呢?请看以下示例:

这里发生的情况并不太像您可能从之前的描述中所预期的那样。当这些活动运行时,“运行 .Net 脚本”活动生成 10 个令牌并将它们逐个传递给第一个“发送事件日志消息”活动,因为后者将会处理它们。有趣的是,第一个“发送事件日志消息”活动并不会立即将令牌传递给下一个活动。它会将所有令牌一直保留到从“运行 .Net 脚本”活动接收完令牌为止,在它处理完最后一个令牌后,才开始发送到下一个活动。因此,相应的流程如下所示。

  1. 初始化数据
  2. 运行 .Net 脚本
  3. 发送事件日志消息
  4. 发送事件日志消息
  5. 发送事件日志消息
  6. 发送事件日志消息
  7. 发送事件日志消息
  8. 发送事件日志消息
  9. 发送事件日志消息
  10. 发送事件日志消息
  11. 发送事件日志消息
  12. 发送事件日志消息
  13. 发送事件日志消息 (2)
  14. 发送事件日志消息 (2)
  15. 发送事件日志消息 (2)
  16. 发送事件日志消息 (2)
  17. 发送事件日志消息 (2)
  18. 发送事件日志消息 (2)
  19. 发送事件日志消息 (2)
  20. 发送事件日志消息 (2)
  21. 发送事件日志消息 (2)
  22. 发送事件日志消息 (2)

只要您了解它是按此方式工作的,您就不会感到惊讶,并且可以围绕此功能相应地构建您的 Runbook。同时从一个活动在多个方向创建分支会怎么样呢?这些活动会并行运行吗?因为它们仍位于同一 Runbook 中,所以回答是否定的。请看以下 Runbook:

此 Runbook 执行时,这些活动按此顺序运行:

  1. 初始化数据
  2. 运行 .Net 脚本
  3. 发送事件日志消息
  4. 发送事件日志消息 (2)
  5. 发送事件日志消息
  6. 发送事件日志消息 (2)
  7. 发送事件日志消息
  8. 发送事件日志消息 (2)
  9. 发送事件日志消息
  10. 发送事件日志消息 (2)
  11. 发送事件日志消息
  12. 发送事件日志消息 (2)
  13. 发送事件日志消息
  14. 发送事件日志消息 (2)
  15. 发送事件日志消息
  16. 发送事件日志消息 (2)
  17. 发送事件日志消息
  18. 发送事件日志消息 (2)
  19. 发送事件日志消息
  20. 发送事件日志消息 (2)
  21. 发送事件日志消息
  22. 发送事件日志消息 (2)

在此实例中,“运行 .Net 脚本”活动会在出站链路中交替它发送到各个方向的令牌。好了,您可能会问,当您合并两个版本并且您的分支结构包含多个后续活动时会怎样呢?具体如下:

在此示例中,我将使用“运行 .Net 脚本”活动写入事件日志,这是因为我可以更好地控制源名称并在查看大量事件时提供更便于阅读的输出。那么,当我运行此活动时会怎样呢?事件日志如下所示:

正如您所看到的,此模式遵循我之前演示的原始分支 Runbook 模型,从分支 1 到分支 2 来回交替,直到它从主脚本活动发送完令牌为止。随后它合并之前多个后续活动示例的功能,因为每个“A”活动(1a 和 2a)会收集所有令牌直到接收完所有令牌为止,然后将令牌发送到下一个活动,并且这些分支会再次交替,以便活动 1b 和 2b 以交替方式运行,直到完成整个 Runbook 为止。

因此,可以从此模式得出的预期是,如果您有一个非独占分支(也称为拆分),分支的两侧在运行时均不需要条件或者两个条件均为 true,那么您最终将以交替方式运行活动,直到它们通过“接合”活动重新连接为止。

再向前推进一步(因为我想知道),当您的 Runbook 中有多个分支时会怎么样呢?其工作原理是什么呢?让我们来看一个例子:

现在查看事件日志:

同样的模式再次出现,即先执行所有“A”活动,然后执行“B”活动。但要注意第二个分支的处理方式。当它到达第二个分支时,“分支 1”现在为每个“分支 2 运行”活动执行两项活动。这意味着,当某个活动运行并将其令牌传递到 Runbook 中的下一个项目时,它实际上将评估所有传出链路条件并将这些条件作为一个集合处理,然后将命令让与另一个分支。

并行处理

那么,您如何让 Runbook 以并行方式进行处理呢?在单个 Runbook 中,无法做到这一点。它将父 Runbook 和子 Runbook 进行结合,即使这样,它也不是*真正*并行的,它只是*基本上*是并行的。我会进行解释 … 您只需将要并行运行的活动放在一个单独的 Runbook 中,然后使用“调用 Runbook”活动调用它即可。

然后您需要再执行两项操作。首先,您需要确保“调用 Runbook”活动取消选中了“等待完成”框。

接下来,您需要设置子 Runbook 的属性以允许执行多个并发 Runbook。右键单击 Runbook 选项卡并选择“属性”,然后单击“作业并发”选项卡并将允许的并发 Runbook 数量设置为更高的数值。

注意:您在此处设置的数量仍然限制为 Runbook Server 上运行的所有 Runbook 的最大数量,后者由限制值确定。有关该设置的更多信息,请参阅如何配置 Runbook 限制

当您取消选中“等待完成”框时,它基本上会触发另一个 Runbook 启动,一旦触发,它就会将该操作视为已完成并启动下一个 Runbook,而不管上一个 Runbook 是否正在执行。因此,尽管子 Runbook 的启动是按顺序进行的,但启动速度非常快以致于感觉就像并行进行的,并且由于每个子 Runbook 在其各自的 policymodule 进程中运行,因此它可以独立于其他 Runbook 运行并且可以并行运行。下面是上述 Runbook 的事件日志输出:

正如您所看到的,子 Runbook 的每个实例都在同一秒内运行并创建了一个事件日志条目。如果这不是并行执行,我就不知道这是什么了

希望这能帮助您更好地了解 Runbook 活动的流程以及数据如何在 Runbook 中的这些活动之间传递。欢迎创建您自己的测试 Runbook 并试用您自己的方案。