.NET Framework 4 包括 Windows Workflow Foundation(WF)的重大修订,对性能进行了大量投资。 此新修订引入了与作为 .NET Framework 3.0 和 .NET Framework 3.5 一部分发布的早期版本的 WF 有关的重大设计更改。 它已从编程模型、运行时和工具的核心重新构建,以大大提高性能和可用性。 本主题介绍这些修订的重要性能特征,并将它们与以前的版本进行比较。
在 WF3 到 WF4 期间,单个工作流组件的性能提高了数倍。 这使得手动编码的 Windows Communication Foundation (WCF) 服务和 WCF 工作流服务之间的差距很小。 WF4 中的工作流延迟明显减少。 持久性性能已提高了2.5到3.0倍。 通过工作流跟踪进行健康监测,开销明显减少。 这些都是在应用程序中迁移到或采用 WF4 的令人信服的理由。
术语
在本主题的其余部分中,.NET Framework 4 中引入的 WF 版本将称为 WF4。 WF 在 .NET Framework 3.0 中引入,并通过 .NET Framework 3.5 SP1 进行了一些次要修订。 对于本主题的其余部分,工作流基础的 .NET Framework 3.5 版本将称为 WF3。 WF3 与 WF4 并排在 .NET Framework 4 中。 有关将 WF3 项目迁移到 WF4 的详细信息,请参阅: Windows Workflow Foundation 4 迁移指南。
Windows Communication Foundation (WCF)是Microsoft用于构建面向服务的应用程序的统一编程模型。 它首先作为 .NET Framework 3.0 和 WF3 的一部分引入,现在是 .NET Framework 的关键组件之一。
Windows Server AppFabric 是一组集成技术,可更轻松地生成、缩放和管理在 IIS 上运行的 Web 和复合应用程序。 它提供用于监视和管理服务和工作流的工具。 有关详细信息,请参阅 Windows Server AppFabric 1.0。
目标
本主题的目标是显示 WF4 的性能特征,以及针对不同方案测量的数据。 它还提供 WF4 和 WF3 之间的详细比较,从而显示了此新修订中所做的重大改进。 本文中介绍的方案和数据量化了 WF4 和 WF3 的不同方面的基本成本。 此数据有助于了解 WF4 的性能特征,有助于规划从 WF3 迁移到 WF4 或在应用程序开发中使用 WF4 的迁移。 但是,应注意本文中介绍的数据得出的结论。 复合工作流应用程序的性能高度取决于工作流的实现方式以及不同组件的集成方式。 必须测量每个应用程序以确定该应用程序的性能特征。
WF4 性能增强功能概述
WF4 经过精心设计和实现,其性能和可伸缩性如下部分所述。
WF 运行时
WF 运行时的核心是一个异步计划程序,用于推动工作流中的活动执行。 它为活动提供高性能、可预测的执行环境。 环境拥有关于执行、延续、完成、取消、异常处理和可预测线程模型的明确协议。
与 WF3 相比,WF4 运行时具有更高效的计划程序。 它利用用于 WCF 的同一 I/O 线程池,这对于执行批处理的工作项非常高效。 内部工作项计划程序队列针对最常见的使用模式进行优化。 WF4 运行时还以非常轻量的方式管理执行状态,使用最少的同步和事件处理逻辑,而 WF3 则依赖于重型事件注册和调用来执行复杂的状态转换。
数据存储和流
在 WF3 中,与活动关联的数据通过类型 DependencyProperty实现的依赖属性建模。 依赖项属性模式是在 Windows Presentation Foundation(WPF)中引入的。 通常,此模式非常灵活,支持简单的数据绑定和其他 UI 功能。 但是,模式要求将属性定义为工作流定义中的静态字段。 每当 WF 运行时设置或获取属性值时,它都会涉及重重的查找逻辑。
WF4 使用明确的数据范围逻辑来大大提高工作流中数据的处理方式。 它通过使用两个不同的概念(变量和参数)将活动中存储的数据与流经活动边界的数据分开。 通过使用变量的明确分层范围以及“In/Out/InOut”参数,活动中数据使用的复杂性显著降低,数据的生命周期也被自动限定。 活动具有由其自变量描述的定义完善的签名。 只需检查某个活动,即可确定它期望接收哪些数据,以及由于执行而生成哪些数据。
在 WF3 中,创建工作流时即会初始化活动。 在 WF 4 中,仅在相应活动执行时才会初始化这些活动。 这样,在创建新的工作流实例时,无需执行 Initialize/Uninitialize 操作即可简化活动生命周期,从而实现更高的效率。
控制流
与任何编程语言一样,WF 通过引入一组用于排序、循环、分支和其他模式的控制流活动,为工作流定义提供控制流支持。 在 WF3 中,当需要重新执行同一活动时,会创建一个新 ActivityExecutionContext 活动,并通过基于 BinaryFormatter的重量级序列化和反序列化逻辑克隆活动。 迭代控制流的性能通常比执行一系列活动要慢得多。
WF4 处理这一点的方式大相径庭。 它采用活动模板,创建新的 ActivityInstance 对象,并将其添加到计划程序队列。 此过程仅涉及显式对象创建,并且非常轻量级。
异步编程
对于长时间运行的阻塞操作(例如 I/O 或分布式计算操作),异步编程适用于应用程序通常具有更好的性能和可伸缩性。 WF4 通过基本活动类型AsyncCodeActivityAsyncCodeActivity<TResult>提供异步支持。 运行时原生理解异步活动,因此能够在异步工作未完成时自动将实例置于无持久化区。 自定义活动可以从这些类型派生,以执行异步工作,而无需保存工作流计划程序线程并阻止可能并行运行的任何活动。
消息传送
最初,WF3 通过外部事件或 Web 服务调用提供非常有限的消息支持。 在 .NET Framework 3.5 中,工作流可以实现为 WCF 客户端,或通过 SendActivity 和 ReceiveActivity 将其公开为 WCF 服务。 在 WF4 中,通过 WCF 消息传送逻辑与 WF 紧密集成,进一步加强了基于工作流的消息传送编程的概念。
.NET 4 中 WCF 中提供的统一消息处理管道可帮助 WF4 服务具有比 WF3 更好的性能和可伸缩性。 WF4 还提供更丰富的消息传送编程支持,可对复杂的消息交换模式(MEP)进行建模。 开发人员可以使用类型化服务协定来实现简单的编程或非类型化服务协定,以实现更好的性能,而无需支付序列化成本。 通过 WF4 中的 SendMessageChannelCache 类提供的客户端通道缓存支持,可以帮助开发人员轻松构建快速应用程序。 有关详细信息,请参阅 更改发送活动的缓存共享级别。
声明性编程
WF4 提供了一个简洁简单的声明性编程框架来为业务流程和服务建模。 编程模型支持完全声明式的活动组合,不需要旁置代码,从而大大简化了工作流的编写。 在 .NET Framework 4 中,基于 XAML 的声明性编程框架已统一到单个程序集 System.Xaml.dll 以支持 WPF 和 WF。
在 WF4 中,XAML 提供真正的声明性体验,并允许在 XML 标记中定义整个工作流定义,引用使用 .NET 生成的活动和类型。 这在使用 XOML 的 WF3 中则很难实现,因为这种格式没有自定义的代码隐藏逻辑。 .NET 4 中的新 XAML 堆栈在序列化/反序列化工作流项目方面具有更好的性能,并使声明性编程更具吸引力和坚实性。
工作流设计器
WF4 的完全声明性编程支持显式对大型工作流的设计时性能提出了更高的要求。 WF4 中的工作流设计器对于大型工作流具有比 WF3 更好的可伸缩性。 借助 UI 虚拟化支持,设计器可以在几秒钟内轻松加载 1000 个活动的大型工作流,而几乎不可能使用 WF3 设计器加载几百个活动的工作流。
组件级性能比较
本部分包含有关 WF3 和 WF4 工作流中各个活动之间的直接比较数据。 持久性等关键领域比单个活动组件对性能产生更深远的影响。 不过,WF4 中各个组件的性能改进非常重要,因为组件现在足够快,足以与手动编码的业务流程逻辑进行比较。 下一部分介绍了一个示例:“服务组合方案”。
环境设置
上图显示了用于组件级性能度量的计算机配置。 一台服务器和五个客户端通过一个 1 Gbps 以太网网络接口连接。 为了简化测量,服务器配置为使用运行 Windows Server 2008 x86 的双处理器/四核服务器中的一个内核。 系统 CPU 利用率保持在接近 100%。
测试详细信息
WF3 CodeActivity 可能是可在 WF3 工作流中使用的最简单活动。 活动在后台代码中调用一个方法,工作流程序员可以将自定义代码放入其中。 在 WF4 中,没有可以提供与 WF3 CodeActivity 相同功能的直接对应。 请注意,WF4 中有一个 CodeActivity 与 WF3 CodeActivity无关的基类。 鼓励工作流作者创建自定义活动并生成仅限 XAML 的工作流。 在以下测试中,名为 Comment
的活动代替了 WF4 工作流中的空 CodeActivity 。 活动中的 Comment
代码如下所示:
[ContentProperty("Body")]
public sealed class Comment : CodeActivity
{
public Comment()
: base()
{
}
[DefaultValue(null)]
public Activity Body
{
get;
set;
}
protected override void Execute(CodeActivityContext context)
{
}
}
空工作流
此测试使用一个没有子活动的序列工作流。
单个活动
工作流是包含一个子活动的序列工作流。 在 WF3 情况下,活动是 CodeActivity,没有代码;在 WF4 情况下,活动是 Comment
。
While 搭配 1000 次迭代
序列工作流包含一个活动,其中一个 While 子活动位于循环中,该活动不执行任何工作。
复制器与 ParallelForEach 的比较
WF3 中的 ReplicatorActivity 具有顺序和并行两种执行模式。 在顺序模式下,活动的性能类似于 WhileActivity. 对于并行执行,ReplicatorActivity 是最有用的。 WF4 中与此类似的是 ParallelForEach<T> 活动。
下图显示了用于此测试的工作流。 WF3 工作流位于左侧,WF4 工作流位于右侧。
具有五个活动的顺序工作流
此测试旨在显示按顺序执行多个活动的效果。 序列中有五个活动。
事务范围
事务范围测试与其他测试略有不同,即不会为每个迭代创建新的工作流实例。 相反,工作流的结构是一个包含 TransactionScope 活动的 while 循环,而该活动包含一个不执行任何工作的活动。 每运行一批 50 次迭代通过 while 循环,就计为单个操作。
薪酬
WF3 工作流具有一个名为 WorkScope
的可补偿活动。 活动只是实现 ICompensatableActivity 接口:
class WorkScope :
CompositeActivity, ICompensatableActivity
{
public WorkScope() : base() { }
public WorkScope(string name)
{
this.Name = name;
}
public ActivityExecutionStatus Compensate(
ActivityExecutionContext executionContext)
{
return ActivityExecutionStatus.Closed;
}
}
错误处理程序以 WorkScope
活动为目标。 WF4 工作流同样简单。
CompensableActivity 有一个 Body 和一个补偿处理程序。 显式补偿是序列中的下一步。 Body 活动和补偿处理程序活动都是空实现:
public sealed class CompensableActivityEmptyCompensation : CodeActivity
{
public CompensableActivityEmptyCompensation()
: base() { }
public Activity Body { get; set; }
protected override void Execute(CodeActivityContext context) { }
}
public sealed class CompensableActivityEmptyBody : CodeActivity
{
public CompensableActivityEmptyBody()
: base() { }
public Activity Body { get; set; }
protected override void Execute(CodeActivityContext context) { }
}
下图显示了基本补偿工作流。 WF3 工作流位于左侧,WF4 工作流位于右侧。
性能测试结果
所有测试均以每秒工作流数进行测量,事务范围测试除外。 如上所述,WF 运行时性能已全面提高,尤其是在需要执行相同活动(如 while 循环)的多个区域。
服务组合方案
如上一部分“组件级性能比较”所示,WF3 和 WF4 之间的开销大幅减少。 WCF 工作流服务现在几乎可以匹配手动编码的 WCF 服务的性能,但仍具有 WF 运行时的所有优势。 此测试方案将 WCF 服务与 WF4 中的 WCF 工作流服务进行比较。
在线商店服务
Windows Workflow Foundation 的优势之一是能够使用多个服务编写进程。 本例为一个在线商店服务,该服务会协调两个服务调用以采购订单。 第一步是使用订单验证服务验证订单。 第二步是使用仓库服务填充订单。
这两个后端服务(订单验证服务和仓库服务)对于这两个测试保持不变。 发生变化的部分是执行业务流程的在线商店服务。 在某种情况下,该服务会被手动编码为 WCF 服务。 对于另一种情况,该服务在 WF4 中作为 WCF 工作流服务编写。 此测试已关闭特定于 WF 的功能,例如跟踪和持久性。
环境
客户端请求通过来自多台计算机的 HTTP 向在线商店服务发出。 一台计算机承载所有三个服务。 在线商店服务和后端服务之间的传输层为 TCP 或 HTTP。 每秒操作数的测量以对在线商店服务完成的 PurchaseOrder
调用数为基础。 通道池是 WF4 的一项新功能。 在本测试的 WCF 部分,通道池不是一项现成的功能,所以在线商店服务中使用的是简单池技术的手工编码实现。
性能
WF 服务在不使用通道池的情况下连接后端 TCP 服务,对吞吐量的影响为 17.2%。 如果使用通道池,则损失大约为 23.8%。 对于 HTTP,影响要少得多:不使用池时为 4.3%,使用池时为 8.1%。 另外要特别注意的是,使用 HTTP 时通道池提供的好处比较少。
尽管在这个测试中,与手工编码的 WCF 服务相比,WF4 运行时会产生开销,但它可能被视为最坏的情况。 此测试中的两个后端服务几乎没有工作。 在实际的端到端场景中,这些服务会执行更昂贵的操作,例如数据库调用,这使得传输层的性能影响显得不那么重要。 此外,WF4 中提供的功能的优势使 Workflow Foundation 成为创建业务流程服务的可行选择。
关键性能注意事项
本节中的功能区域(互操作性除外)在 WF3 和 WF4 之间发生了显著变化。 这会影响工作流应用程序的设计和性能。
工作流激活延迟
在 WCF 工作流服务应用程序中,启动新工作流或加载现有工作流的延迟非常重要,因为它可能会阻止。 在典型场景中,此测试用例测量 WF3 XOML 主机相对于 WF4 XAMLX 主机的性能。
环境设置
测试设置
在此场景中,客户端计算机通过基于上下文的关联来联系 WCF 工作流服务。 上下文关联需要特殊的上下文绑定,并使用上下文标头或 Cookie 将消息与正确的工作流实例相关联。 它具有性能优势,即相关 ID 位于消息标头中,因此不需要分析消息正文。
该服务将使用请求创建新的工作流并发送即时响应,以便测量延迟不包括运行工作流所用的时间。 WF3 工作流是包含后台代码的 XOML,WF4 工作流完全是 XAML。 WF4 工作流如下所示:
活动 Receive 创建工作流实例。 收到的消息中传递的值在回复消息中回显。 答复后的序列包含工作流的其余部分。 在上述情况下,只会显示一个 Comment 活动。 评论活动数已更改,以模拟工作流的复杂性。 评论活动相当于一个不执行任何工作的 WF3 CodeActivity 。 有关评论活动的详细信息,请参阅本文前面的“组件级性能比较”部分。
测试结果
WCF 工作流服务的冷和暖延迟:
在上一个图表中,“冷”是指在给定工作流中不存在 WorkflowServiceHost 的情况。 换句话说,冷延迟是指首次使用工作流,需要编译 XOML 或 XAML。 热延迟是当工作流类型已经编译时创建新工作流实例的时间。 工作流的复杂性在 WF4 案例中几乎没有区别,但在 WF3 案例中具有线性进展。
相关吞吐量
WF4 引入了基于内容的新关联功能。 WF3 仅提供基于上下文的关联。 基于上下文的关联只能通过特定的 WCF 通道绑定完成。 使用这些绑定时,工作流 ID 将插入到消息标头中。 WF3 运行时只能按其 ID 标识工作流。借助基于内容的相关性,工作流作者可以从相关数据(例如帐户号或客户 ID)中创建相关密钥。
基于上下文的相关性具有性能优势,因为相关键位于消息标头中。 无需取消序列化/消息复制即可从消息中读取密钥。 在基于内容的相关性中,相关密钥存储在消息正文中。 XPath 表达式用于查找密钥。 此额外处理的成本取决于消息的大小、正文中的键深度和键数。 此测试比较上下文和基于内容的关联,还显示使用多个键时性能下降。
环境设置
测试设置
上一个工作流与 在“持久性 ”部分中使用的工作流相同。 对于没有持久性的关联测试,运行时中未安装持久性提供程序。 相关性发生在两个位置:CreateOrder 和 CompleteOrder。
测试结果
此图显示性能下降,因为基于内容的相关性中使用的键数增加。 TCP 和 HTTP 之间的曲线相似性表示与这些协议关联的开销。
与持久性的关联
使用持久化工作流时,基于内容的相关性的 CPU 压力从工作流运行时转移到 SQL 数据库。 SQL 持久性提供程序中的存储过程执行匹配键以查找相应工作流的工作。
基于上下文的相关性仍然比基于内容的相关性更快。 但是,差异不那么明显,因为持久性对性能的影响比相关性更大。
复杂的工作流吞吐量
工作流的复杂性不仅按活动数来衡量。 复合活动可以包含多个子活动,这些子活动可能也是复合活动。 随着嵌套级别的数量增加,当前处于执行状态的活动数和可处于状态的变量数也会增加。 此测试比较执行复杂工作流时 WF3 和 WF4 之间的吞吐量。
测试设置
这些测试是在一台配有 4GB RAM 的 Intel Xeon X5355 @ 2.66GHz 4 路计算机上执行的,该计算机运行 Windows Server 2008 x64。 测试代码在单个进程中运行,每个核心有一个线程,达到 100% CPU 利用率。
为此测试生成的工作流有两个主要变量:每个序列中的深度和活动数。 每个深度级别包括并行活动、while 循环、决策、赋值和序列。 在下图所示的 WF4 设计器中,显示了顶级流程图。 每个流程图活动都类似于主流程图。 在设计此工作流时,考虑分形可能很有帮助,其中深度仅限于测试的参数。
给定测试中的活动数取决于每个序列的活动深度和数量。 以下公式计算 WF4 测试中的活动数:
WF3 测试的活动计数可以使用略有不同的公式计算,因为有一个额外的序列:
其中 d 是深度,a 是每个序列的活动数。 这些公式背后的逻辑是,第一个常量乘以序列数,第二个常量是当前级别的静态活动数。 每个流程图中有三个流程图子活动。 在底部深度级别,这些流程图为空,但在其他级别,它们是主流程图的副本。 下表显示了每个测试变体工作流定义中的活动数:
工作流定义中的活动数量随每个深度级别急剧增加。 但在给定的工作流实例中,每个决策点只执行一个路径,因此只执行一小部分实际活动。
为 WF3 创建了等效的工作流。 WF3 设计器在设计区域显示整个工作流,而不是显示嵌套结构,因此由于它过大而未在本主题中显示。 下面显示了工作流片段。
若要在极端情况下练习嵌套,此测试中的另一个工作流使用 100 个嵌套序列。 最内层的序列中是一个 Comment
或 CodeActivity。
跟踪和持久性不用作此测试的一部分。
测试结果
即使具有大量深度和大量活动的复杂工作流,性能结果也与本文前面显示的其他吞吐量数字一致。 WF4 的吞吐量速度呈现几个数量级的增长,必须在对数刻度上进行比较。
内存
Windows Workflow Foundation 的内存开销在两个关键领域进行度量:工作流复杂性和工作流定义数。 内存度量是在 Windows 7 64 位工作站上进行的。 有多种方法可用于获取工作集大小的度量,例如监视性能计数器、轮询 Environment.WorkingSet,或使用 VMMap 中可用的 VMMap 等工具。 方法的组合用于获取和验证每个测试的结果。
工作流复杂性测试
工作流复杂性测试根据工作流的复杂性来度量工作集差异。 除了上一节中使用的复杂工作流外,还添加了新的变体,以涵盖两种基本情况:单个活动工作流和包含 1000 个活动的序列。 对于这些测试,工作流将在单个串行循环中初始化并执行,持续一分钟。 每个测试变体都运行三次,记录的数据是这三次运行的平均值。
这两个新的基本测试具有如下所示的工作流:
在上面所示的 WF3 工作流中,使用了空的 CodeActivity 活动。 上面的 WF4 工作流使用 Comment
活动。 本文前面的组件级性能比较部分介绍了该 Comment
活动。
此图中要注意的一个明显趋势是嵌套对 WF3 和 WF4 中的内存使用率的影响相对较小。 最重要的内存影响来自给定工作流中的活动数。 根据序列1000、复杂深度5的序列5和复杂深度7的序列1这些变体的数据可以看出,当活动数量达到数千时,内存使用的增加变得更加明显。 在有大约 29000 个活动的极端情况下(深度 7、序列 1),WF4 使用的内存几乎要比 WF3 少 79%。
多个工作流定义测试
测量每个工作流定义的内存被划分为两个不同的测试,这是因为 WF3 和 WF4 提供了不同的选项来托管工作流。 测试以不同于工作流复杂性测试的方式运行,因为给定工作流按定义仅实例化并执行一次。 这是因为工作流定义及其主机在 AppDomain 的生存期内保留在内存中。 在垃圾回收期间,应清理运行给定工作流实例所用的内存。 WF4 的迁移指南包含有关托管选项的更多详细信息。 有关详细信息,请参阅 WF 迁移指南:工作流托管。
可以通过多种方式为工作流定义测试创建许多工作流定义。 例如,可以使用代码生成来创建一组 1000 个工作流,这些工作流除了名称不同,其余都相同,然后将每个工作流保存到单独的文件中。 对于控制台承载的测试采取了这种方法。 在 WF3 中,该 WorkflowRuntime 类用于运行工作流定义。 WF4 可用于 WorkflowApplication 创建单个工作流实例,也可以直接用于 WorkflowInvoker 运行活动,就好像它是方法调用一样。 WorkflowApplication 是单个工作流实例的主机,拥有与 WorkflowRuntime 更接近的功能相似性,因此在此测试中选择了它。
在 IIS 中托管工作流时,可以使用 a VirtualPathProvider 创建一个新 WorkflowServiceHost 工作流,而不是生成所有 XAMLX 或 XOML 文件。 VirtualPathProvider 处理传入请求,并响应一个“虚拟文件”,这个文件可以从数据库加载,或者在本例中即时生成。 因此,无需创建 1000 个物理文件。
控制台测试中使用的工作流定义是具有单个活动的简单顺序工作流。 对于 WF3 事例,单个活动为空 CodeActivity ,WF4 事例为活动 Comment
。 IIS 托管的案例使用了从接收消息开始并在发送回复时结束的工作流:
下图显示了具有 ReceiveActivity 的 WF3 工作流,以及具有请求/响应模式的 WF4 工作流:
下表显示单个工作流定义和 1001 个定义之间的工作集增量:
宿主选项 | WF3 工作集增量 | WF4 工作集增量 |
---|---|---|
控制台应用程序承载的工作流 | 18 MB | 9 MB |
IIS 托管工作流服务 | 446 MB | 364 MB |
由于详细的 WCF 服务项目以及与主机关联的消息处理逻辑,IIS 中的托管工作流定义消耗了更多的内存 WorkflowServiceHost。
对于 WF3 中的主机托管,工作流是在代码中实现的,而不是 XOML。 在 WF4 中,默认值是使用 XAML。 XAML 作为嵌入资源存储在程序集中,并在运行时编译以提供工作流的实现。 此过程会产生一些开销。 为了在 WF3 和 WF4 之间进行公平比较,使用编码的工作流而不是 XAML。 下面显示了其中一个 WF4 工作流的示例:
public class Workflow1 : Activity
{
protected override Func<Activity> Implementation
{
get
{
return new Func<Activity>(() =>
{
return new Sequence
{
Activities = {
new Comment()
}
};
});
}
set
{
base.Implementation = value;
}
}
}
还有其他许多因素可能会影响内存消耗。 相同的建议依然适用于所有管理程序。 在 IIS 托管的环境中, WorkflowServiceHost 为工作流定义创建的对象保留在内存中,直到应用程序池被回收。 编写扩展时,应记住这一点。 此外,最好避免“全局”变量(作用域为整个工作流的变量),并尽可能限制变量的范围。
工作流运行时服务
持久性
WF3 和 WF4 都附带了 SQL 持久性提供程序。 WF3 SQL 持久性提供程序是一个简单的实现,它序列化工作流实例并将其存储在 Blob 中。 因此,此提供程序的性能在很大程度上取决于工作流实例的大小。 在 WF3 中,实例大小可能会由于许多原因而增加,如本文前面所述。 许多客户选择不使用默认 SQL 持久性提供程序,因为将序列化实例存储在数据库中不会显示工作流的状态。 若要查找特定工作流而不了解工作流 ID,必须反序列化每个持久化实例并检查内容。 许多开发人员更喜欢编写自己的持久性提供程序来克服此障碍。
WF4 SQL 持久性提供程序试图解决其中一些问题。 持久性表格会公开某些信息,比如活跃的书签和可推广的属性。 WF4 中基于内容的新关联功能使用 WF3 SQL 持久性方法无法很好地执行,该方法导致持久化工作流实例的组织发生了一些变化。 这使得持久性提供程序的工作更加复杂,并给数据库带来额外的压力。
环境设置
测试设置
即使在改进的功能集和更好的并发处理的情况下,WF4 中的 SQL 持久性提供程序也比 WF3 中的提供程序更快。 为了展示这一点,下面比较了两个在 WF3 和 WF4 中执行基本相同操作的工作流。
这两个工作流都是由收到的消息创建的。 发送初始答复后,工作流将被持久化。 在 WF3 示例中, TransactionScopeActivity 空用于启动持久性。 在 WF3 中,可以通过将活动标记为“关闭时暂留”来实现相同的结果。另一个关联的消息会完成工作流。 工作流将持久保存,但未卸载。
测试结果
当客户端层和中间层之间的传输为 HTTP 时,WF4 中的持久性显示 2.6 倍的改进。 TCP 传输将系数增加到 3.0 倍。 在所有情况下,中间层上的 CPU 使用率为 98% 或更高。 WF4 吞吐量更大的原因是工作流运行时速度更快。 对于这两种情况,序列化实例的大小较低,在这种情况下不是主要贡献元素。
此测试中的 WF3 和 WF4 工作流都使用活动显式指示何时应发生持久性。 这样可以在不卸载工作流的情况下持久保存工作流。 在 WF3 中,也可以使用该功能持久保存 TimeToUnload ,但这会将工作流实例从内存中卸载。 如果使用 WF3 的开发人员希望确保工作流在某些点保留,则它们必须更改工作流定义或支付卸载和重新加载工作流实例的费用。 WF4 中的新功能使无需卸载即可持久保存: TimeToPersist 此功能允许工作流实例在空闲时持久保存,但保留在内存中,直到 TimeToUnload 达到阈值或恢复执行为止。
请注意,WF4 SQL 持久性提供程序在数据库层中执行更多工作。 SQL 数据库可能会成为瓶颈,因此监视那里的 CPU 和磁盘使用情况非常重要。 在性能测试工作流应用程序时,请务必包括 SQL 数据库中的以下性能计数器:
PhysicalDisk\%Disk 读取时间
PhysicalDisk\% 磁盘时间
PhysicalDisk\% 磁盘写入时间
PhysicalDisk\% 平均磁盘队列长度
物理磁盘\平均磁盘读取队列长度
PhysicalDisk\Avg. Disk Write Queue Length
PhysicalDisk\Current Disk Queue Length
处理器信息\% 处理器时间
SQLServer:Latches\Average Latch Wait Time (ms)
SQLServer:Latches\Latch Waits/sec
跟踪
工作流跟踪可用于跟踪工作流的进度。 跟踪事件中所包含的信息由跟踪配置文件决定。 跟踪配置文件越复杂,跟踪的成本就越高。
WF3 随基于 SQL 的跟踪服务一起提供。 此服务可以在批处理和非批处理模式下工作。 在非批处理模式下,跟踪事件将直接写入数据库。 在批处理模式下,跟踪事件将收集到与工作流实例状态相同的批处理中。 批处理模式对最广泛的工作流设计具有最佳性能。 但是,如果工作流在未持久化的情况下运行许多活动,并且这些活动被跟踪,则批处理可能会对性能产生负面影响。 这种情况通常发生在循环中,避免这种情况的最佳方法是设计大型循环以包含持久性点。 将持久性点引入循环也会对性能产生负面影响,因此衡量每个循环的成本并产生平衡非常重要。
WF4 未随 SQL 跟踪服务一起提供。 将跟踪信息记录到 SQL 数据库最好通过应用程序服务器处理,而不是内置在 .NET Framework 中。 因此,SQL 跟踪现在由 AppFabric 处理。 WF4 中现成的跟踪提供程序以 Windows 事件跟踪 (ETW) 为基础。
ETW 是内置于 Windows 中的内核级低延迟事件系统。 它使用提供者/使用者模型,使之能在有实际使用者时才使事件跟踪产生负面影响。 除了处理器、磁盘、内存和网络使用情况等内核事件外,许多应用程序还利用 ETW。 ETW 事件比性能计数器更强大,因此可以将事件自定义到应用程序。 事件可以包含文本,例如工作流 ID 或信息性消息。 此外,事件使用位掩码进行分类,以便使用特定事件子集比捕获所有事件具有更少的性能影响。
使用 ETW 进行跟踪而不是 SQL 的方法的好处包括:
跟踪事件的集合可以分隔到另一个进程。 这为记录事件的方式提供了更大的灵活性。
ETW 跟踪事件可以轻松地与 WCF ETW 事件或其他 ETW 提供程序(例如 SQL Server 或内核提供程序)结合使用。
工作流作者不需要更改工作流,以便更好地处理特定的跟踪实现,例如 WF3 SQL 跟踪服务的批处理模式。
管理员无需回收主机进程就可以打开或关闭跟踪。
ETW 跟踪的性能优势有一个缺点。 如果系统面临巨大的资源压力,ETW 事件可能会丢失。 事件处理不是为了阻止正常的程序执行,因此不能保证所有 ETW 事件都将广播到其订阅者。 这使 ETW 跟踪非常适合运行状况监视,但不适合审核。
虽然 WF4 没有 SQL 跟踪提供程序,但 AppFabric 确实有。 AppFabric 的 SQL 跟踪方法是使用一个 Windows 服务来订阅 ETW 事件,该服务会将事件收集起来,并批量写入一个专为快速插入设计的 SQL 表中。 单独的作业会清空此表中的数据,并将其改革为可在 AppFabric 仪表板上查看的报告表。 这意味着一批跟踪事件会与其来源的工作流分开处理,因此不用等待暂留点就可以进行记录。
可以使用 logman 或 xperf 等工具记录 ETW 事件。 可以使用 xperfview 之类的工具查看压缩的 ETL 文件,也可以使用 tracerpt 转换为更可读的格式(如 XML)。 在 WF3 中,获取没有 SQL 数据库的跟踪事件的唯一选项是创建自定义跟踪服务。 有关 ETW 的详细信息,请参阅 WCF 服务和适用于 Windows 的事件跟踪 和 事件跟踪 - Windows 应用程序。
启用工作流跟踪将不同程度地影响性能。 下面的基准使用 logman 工具来处理 ETW 跟踪事件,并将其记录到 ETL 文件中。 AppFabric 中的 SQL 跟踪成本不在本文的范围内。 基本跟踪配置文件(也会在 AppFabric 中使用)会在此基准测试中显示。 此外,还会包括只跟踪运行状况监视事件的成本。 这些事件可用于排查问题并确定系统的平均吞吐量。
环境设置
测试结果
运行状况监视对吞吐量大约有 3% 的影响。 基本配置文件的成本大约为 8%。
互作
WF4 几乎完全重写了 WF,因此 WF3 工作流和活动与 WF4 不兼容。 许多早期采用 Windows Workflow Foundation 的客户将具有 WF3 的内部或第三方工作流定义和自定义活动。 简化到 WF4 的转换的一种方法是使用互作活动,该活动可以从 WF4 工作流中执行 WF3 活动。 建议仅在必要时使用Interop活动。 有关迁移到 WF4 的详细信息,请查看 WF4 迁移指南。
环境设置
测试结果
下表显示了在各种配置中按顺序运行包含五个活动的工作流的结果。
测试 | 吞吐量(工作流/秒) |
---|---|
WF3 运行时中的 WF3 序列 | 1,576 |
使用 Interop 的 WF4 运行时中的 WF3 序列 | 2,745 |
WF4 序列 | 153,582 |
可以注意到,使用 Interop 与直接使用 WF3 相比有显著的性能提升。 但是,与 WF4 活动相比,增加是微不足道的。
概要
WF4 对性能的巨额投资在许多关键领域得到了回报。 与 WF3 相比,单个工作流组件性能在某些情况下比 WF3 快数百倍,因为 WF 运行时更精简。 延迟数字也显著提高。 这意味着,与手动编码 WCF 业务流程服务相比,使用 WF 的性能损耗非常小,因为使用 WF 还有额外的优势。 持久性性能已提高了2.5到3.0倍。 通过工作流跟踪进行健康监控,现在几乎没有任何负担。 针对正考虑从 WF3 迁移到 WF4 的用户,可以使用一套全面的迁移指南。 所有这些作都应使 WF4 成为编写复杂应用程序的有吸引力的选项。