工作流服务

使用 Windows Server AppFabric 创建长时间运行的可伸缩工作流

Rafael Godinho

下载代码示例

业务流程可能会涉及各种各样的应用场景。它们可以包括人工流程、通过服务公开的业务逻辑、表示层的协调甚至应用程序集成。

尽管这些场景各不相同,成功的业务流程还是有一些共同之处的。它们必须很容易构建、使用和修改。它们必须可伸缩,能够满足不断变化的业务需求。并且,它们通常还需要进行某种形式的状态记录、合规性和调试。

工作流就是一个编写成应用程序的业务流程的很好例子。它们包含了我提到的所有这些元素:人的业务需求、业务逻辑、人与应用程序之间的协调以及轻松输入数据和检索状态的功能。应用程序有很多工作要做,因此需要编写很多代码。

幸运的是,Microsoft .NET Framework 和 Windows Server AppFabric 提供了全套工具,可帮助您创建、部署和配置长时间运行的、可跟踪的工作流服务。您可能已经很熟悉 .NET Framework。Windows Server AppFabric 是一组 Windows Server 扩展,其中包括针对基于 Windows Communication Foundation (WCF) 和 Windows Workflow Foundation (WF) 的服务的缓存和托管服务。

本文将向您介绍使用 WCF、WF 和 Windows Server AppFabric 构建简单可伸缩工作流服务的过程。

创建工作流服务

若要创建工作流服务,您需要综合使用两项技术:WCF 和 WF。这种集成对开发人员来说是无缝的,其实现方式是通过使用特定的消息传送活动在工作流中接收 WCF 消息。工作流托管在各个工作流的 WCF ServiceHost (WorkflowServiceHost) 中,为这些消息提供了 WCF 端点。在消息传送活动组中,有两种活动可用来接收信息,从而使工作流能够在发生 Web 服务调用时从外部客户端接收消息:Receive 活动和 ReceiveAndSendReply 模板。

Receive 活动用于接收要由工作流处理的信息。它几乎可以接收所有类型的数据,例如内置数据类型、应用程序定义的类,甚至是 XML 序列化的类型。图 1 在工作流设计器中显示了一个 Receive 活动的示例。

图 1 工作流设计器上的 Receive 活动

这种活动具有大量属性,但其中四个属性尤为重要,应该加以注意:

  • CanCreateInstance 用于确定工作流运行时是必须创建新的工作流实例来处理传入的消息,还是可以通过关联技术重复利用现有的工作流实例。稍后我将更详细地讨论关联。您可能希望在工作流的第一个 Receive 活动上,将其设置为 True。
  • OperationName 指定由此 Receive 活动实现的服务操作名称。
  • Content 指示要由服务接收的数据。这与 WCF 服务操作合约参数极其相似。
  • ServiceContractName 用于在所生成的 Web 服务描述语言 (WSDL) 内创建服务合约分组服务操作。

如果单独使用 Receive 活动,则它将实现单向消息交换模式,用于从客户端接收信息,而不向客户端发送回复。通过将 Receive 活动与 SendReply 活动相关联,这种活动也用于实现请求-响应模式。

为了帮助实现请求-响应模式,WF 向 Visual Studio 工具箱中添加了一个名为 ReceiveAndSendReply 的选项。将此选项放到工作流设计器上时,它会自动在 Sequence 活动内创建一对预先配置好的 Receive 和 SendReplyToReceive 活动(请参见图 2)。

图 2 工作流设计器上的 ReceiveAndSendReply

ReceiveAndSendReply 模板背后的原理是在 Receive 和 SendReplyToReceive 操作之间执行一些处理操作。但是,特别要注意的是,在 Receive 和 SendReplyToReceive 对之间不允许持久性。它将创建一个非持久的区域,该区域仅持续到两项活动均结束时。这意味着如果工作流实例变为空闲,即使主机被配置为在工作流空闲时仍然保留工作流,该工作流仍然不会保留。如果某项活动尝试在非持久区域中显式保留工作流实例,将引发致命异常,工作流将中止,并且会向调用者返回异常。

关联调用

有时,业务流程可能会收到多个外部调用。在出现这种情况时,会在第一次调用时创建一个新的工作流实例,接着执行该工作流的活动,然后该工作流就会进入空闲状态,等待后续的调用。当后续调用达到时,该工作流实例将结束空闲状态,并继续执行。

通过这种方式,工作流运行时必须有一种方法,能够使用其在后续调用中收到的信息,并且能够将此信息与以前创建的工作流实例相区别,从而继续处理。否则,它可能会调用任意实例,从而使整个流程的一致性面临风险。这称为关联,您可以将后续调用关联到与该调用相关的待定工作流。

关联表现为一种 XPath 查询,它可以识别特定消息中的特定数据。可以使用 InitializeCorrelation 活动或通过向类似下列活动的 CorrelationInitializers 属性指定值,对关联进行初始化:Receive、SendReply、Send 和 ReceiveReply。

此初始化过程可以在代码中完成,也可以从 Visual Studio 2010 中使用工作流设计器来完成。因为 Visual Studio 提供了一个向导,可帮助创建 XPath 查询,所以这种方法对大多数开发人员来说都更简单,也更愿意采用。

可能需要使用关联的一种情况就是开支报告工作流。首先,某位员工提交开支报告数据。随后,其经理可以审核该报告,并且批准或拒绝该支出(请参见图 3)。

图 3 开支报告示例场景

在此场景中,在工作流向员工的客户端应用程序返回响应时,就会创建关联。若要创建关联,您需要一些能够标识上下文的信息,例如开支报告 ID(可能已经是唯一 ID)。然后,工作流示例变为空闲,等待经理批准或拒绝开支报告。当经理的客户端应用程序发出批准调用时,工作流运行时会将收到的开支报告 ID 与以前创建的工作流实例相关联,从而继续流程。

若要在 Visual Studio 2010 中创建关联,请先在工作流设计器中选择要用来初始化关联的活动。在我的示例中,这是向客户端返回开支报告 ID 的活动。在 SendReply 活动中,我通过单击省略号按钮,在“属性”窗口中设置了 CorrelationInitializers 属性。这将显示“添加相关初始值设定项”对话框(请参见图 4),您可以在此配置关联。

图 4 设置 XPath 查询关联

有三项必须设置:关联句柄、关联类型和 XPath 查询。关联句柄是工作流运行时用来存储关联数据的变量,由 Visual Studio 自动创建。

下一步是设置关联类型。.NET Framework 提供多种类型的关联,但因为我需要查询与客户端交换的部分信息,即基于内容的关联,所以我的最佳选择是使用 Query 关联初始值设定项。完成此操作后,可以将 XPath 查询设置为开支报告 ID。当我单击箭头时,Visual Studio 会检查消息内容,并显示一个列表,用于选择适当的信息。

若要在开支批准后继续执行工作流,相应的 Receive 活动必须使用此关联。通过设置 CorrelatesOn 属性可以实现这一点。请在“属性”窗口中单击属性附近的省略号按钮,以便打开“CorrelatesOn 定义”对话框(请参见图 5)。在此对话框中,需要将 CorrelatesWith 属性设置为用来为 SendReplyToReceive 活动初始化关联的相同句柄,并且必须将 XPath 查询属性设置为通过开支报告批准消息收到的相同键值和开支报告 ID。

图 5 CorrelatesOn 定义

WF 附带了一组通用活动,名为基本活动程序库 (BAL)。我将使用其中部分活动来发送和接收信息。尽管它们非常实用,但有时还是需要使用与业务规则关联更紧密的活动。基于我到目前为止讨论的场景,需要使用三种活动来提交和批准开支报告:Create、Approve 和 Deny 开支报告。由于这些活动都非常相似,我将只展示 CreateExpenseReportActivity 的代码:

public sealed class CreateExpenseReportActivity 
  : CodeActivity<int> {
  public InArgument<decimal> Amount { get; set; }
  public InArgument<string> Description { get; set; }
  protected override int Execute(CodeActivityContext context) {
    Data.ExpenseReportManager expenseReportManager = 
      new Data.ExpenseReportManager();
    return expenseReportManager.CreateExpenseReport(
      Amount.Get(context), Description.Get(context));
  }
}

该活动接收开支的金额和描述,这两种信息均声明为 InArgument。大部分繁重的工作都在 Execute 方法中完成。它访问一个类,该类使用实体框架来处理数据库访问以及保存开支报告信息,而实体框架在另一端返回开支报告 ID。我只需要执行 CLR 代码,而不需要与 WF 运行时交互,因此最简单的选择是创建继承自 CodeActivity 的活动。完整的工作流如图 6 所示。

图 6 完整的开支报告工作流

托管工作流服务

创建工作流服务之后,您需要决定它在何处运行。传统的选择是在您自己的托管环境中运行,例如 IIS 或 Windows Process Activation Services (WAS)。但另一种选择是充分利用 Windows Server AppFabric。Windows Server AppFabric 针对托管、管理、保护和伸缩通过 WCF 或 WF 创建的服务,增强了 Windows Server 2008 R2 中的“应用程序服务器”角色。您也可以在运行 Windows Vista 或 Windows 7 的 PC 上使用 Windows Server AppFabric,进行开发和测试。

尽管 IIS 和 WAS 已经支持服务托管,Windows Server AppFabric 还是提供了一种更实用、更容易管理的环境,该环境集成了 WCF 和 WF 功能,例如持久性以及通过 IIS 管理器进行跟踪。

简化的工作流持久性

在处理您的所有业务流程时,计算机拥有的资源仍然是有限的,因此没有理由将计算资源浪费在空闲工作流上。对于长期运行的流程,您可能无法控制从流程开始到流程结束所需的总时间。它可能需要几分钟、几小时、几天,甚至更长。如果它依赖于外部实体,例如其他系统或最终用户,则它大部分时间都处于空闲状态,仅仅用来等待响应。

WF 提供了一种持久性框架,能够将工作流实例状态的持久快照(与流程或计算机信息无关)存储到实例存储中。WF 4 本身已经附带一个 SQL Server 实例存储。但是,因为 WF 很容易扩展,我可以自行创建实例存储,以便根据需要保留工作流实例的状态。当工作流实例变为空闲状态并被保留之后,它可以卸载到预留内存和 CPU 资源,最终在服务器场之间的各个节点之间来回移动。

Windows Server AppFabric 有一种简便方法,可以设置和维护与 WF 持久性功能的集成。整个流程对工作流运行时是透明的,而工作流运行时会将持久性任务委托给 AppFabric,从而扩展了默认的 WF 持久性框架。

配置持久性的第一步是使用 Windows Server AppFabric 配置向导或 Windows PowerShell cmdlet 来设置 SQL Server 数据库。如果持久性数据库不存在,该向导可以创建该数据库;也可以仅仅创建 AppFabric 架构。在创建数据库之后,其他所有步骤都在 IIS 管理器中完成。

在 IIS 管理器中,右键单击要配置的节点(服务器、网站或应用程序)并选择“管理 WCF 和 WF 服务”|“配置”以打开“为应用程序配置 WCF 和 WF”对话框,然后单击“工作流暂留”(请参见图 7)。您会看到,您可以选择启用或禁用工作流持久性。

图 7 配置工作流持久性

您还可以设置当工作流变为空闲时,工作流运行时将花多长时间将工作流实例从内存中卸载并将其保留到数据库中。默认值为 60 秒。如果您将该值设为 0,则将立即保留工作流实例。这对于通过负载平衡器进行扩展来说尤其重要。

工作流跟踪

有时,与外部用户或应用程序交互的进程可能会出错。由于长期运行的进程具有独立的特性,在这些场景中情况甚至更糟。在出现问题时,开发人员通常需要分析一批日志,以便找出发生了什么问题、如何再现该问题,以及最重要的,如何更正该问题并使系统保持正常运行。如果您使用的是 WF,这种框架中已经内置了这种日志功能。

同样,WF 具有一个可扩展的框架用于保留空闲实例,它还具有一个可扩展的框架用于显示工作流的执行情况。此框架称为跟踪。在此框架执行期间,它将透明地通知工作流记录关键事件。Windows Server AppFabric 使用此扩展能力来提高内置的 WF 跟踪功能,在 SQL Server 数据库中记录执行过程中的事件。

Windows Server AppFabric 的跟踪配置与用于持久性的跟踪配置类似,可以通过 Windows Server AppFabric 配置向导或 Windows PowerShell cmdlet 进行访问。在前文讨论的“为应用程序配置 WCF 和 WF”对话框中,单击“监视”。现在,您可以选择启用或禁用跟踪,还可以选择跟踪级别,如图 8 所示。

图 8 在 Windows Server AppFabric 上启用跟踪

在 Windows Server AppFabric 中配置跟踪时,您可以选择五种监视级别:

  • “关”与禁用监视具有同样的效果,最适合用在希望尽可能降低跟踪开销的场景。
  • “仅错误”仅显示严重事件,例如错误和警告。此模式最适合仅需要进行最低限度的错误记录的高性能场景。
  • “运行状况监视”是默认的监视级别,它包含“仅错误”级别记录的所有数据,加上一些额外的处理数据。
  • “端对端监控”包含“运行状况监视”级别记录的所有数据,加上用于重构整个消息流的额外信息。此级别用在一个服务调用另一个服务的场景。
  • “故障排除”正如其名,是最详细的级别,对应用程序不能正常运行且需要修复的场景非常有用。

扩展工作流服务

由于 Windows Server AppFabric 扩展了 Windows Server 中的应用程序服务器角色,它从其前身继承了高度可扩展的基础结构,可运行于网络负载平衡器 (NLB) 之后的服务器场中。您也可以看到它能够根据需要保留和跟踪工作流实例。因此,若要托管长期运行的工作流程并支持从客户端发出的大量请求,Windows Server AppFabric 是一种极佳的选择。

工作流服务可扩展环境的示例可参见图 9。它具有两个 Windows Server AppFabric 实例,两者都运行相同的工作流定义的副本。NLB 将请求路由到可用的 AppFabric 实例。

图 9 可扩展环境中的工作流

在开支报告场景中,当客户端首次访问该服务以创建一份开支报告时,平衡器会将请求重定向至一个可用的 Windows Server AppFabric 实例,该实例将开支数据保存到数据库中,将生成的 ID 返回给客户端,并且由于工作流变为空闲状态,等待工作流运行时批准该开支,因而将正在运行的实例保留到数据库中。

随后,当客户端应用程序访问该服务以批准或拒绝开支报告时,NLB 会将请求重定向至一个可用的 Windows Server AppFabric 实例(可与第一次服务调用时的服务器不同),并且服务器将关联该请求,并从持久性数据库中恢复该工作流实例。现在,内存中的实例将继续进行处理,将批准保存到数据库中,然后在完成后返回客户端。

结束语

正如您看到的,在负载平衡环境中,将工作流服务与关联、持久性和跟踪相结合,是在可扩展环境中运行这些服务的一种强大的技术。组合使用这些功能可提高操作效率,允许对正在运行的服务执行主动的操作,以及在线程、进程甚至计算机之间分配工作流。通过这种方式,开发人员就可以创建可全面扩展的解决方案,该方案可在一台计算机上运行,也可以在大型的服务器场中运行,而无需担心基础结构的复杂性。

有关利用 WCF 和 WF 设计工作流的详细信息,请务必阅读 Leon Welicki 的文章“使用 WCF 和 WF 4 的工作流可视化设计”,该文章发表在 MSDN 杂志 的 2010 年 5 月号上 (msdn.microsoft.com/magazine/ff646977)。有关长期运行的进程和工作流持久性的更深入讨论,请参见 Michael Kennedy 的文章“支持长时间运行操作的 Web 应用程序”,该文章发表在 2009 年 1 月号上 (msdn.microsoft.com/magazine/dd296718)。

有关 Windows Server AppFabric 的详细信息,请参见 Windows Server 开发人员中心,网址为 msdn.microsoft.com/windowsserver/ee695849

Rafael Godinho 是 Microsoft Brazil 的 ISV 开发推广人员,他负责帮助本地合作伙伴应用 Microsoft 技术。您可以通过 Godinho 的博客 blogs.msdn.com/rafaelgodinho 与他联系。

衷心感谢以下技术专家对本文的审阅:Dave CliffeRon JacobsLeon Welicki