在顺序 WWF 中重试活动执行





0/5 (0投票)
关于 WWF 的知识分享文章。
引言
本文是写给那些已经知道如何在 WWF (顺序工作流) 中进行开发的人。在这里,我将提供一个解决方案,关于如何在顺序工作流中执行相同的活动,以应对不确定的失败。我之所以找到这个解决方案,是因为我在项目中长期以来都遇到这个问题,并且每次都需要手动完成工作流的编排。
背景
如果您的工作流中有许多活动集,它们调用外部/内部 Web 服务/WCF,并且由于这些 Web 服务/WCF 的内部服务器错误,抛出了 FaultException (WebException),导致您的工作流活动因错误而失败/终止。最终,您的整个工作流都会因此而终止。因为这就是您开发/设计工作流的方式,即如果活动中出现任何问题,就停止在那里。但有些特定情况是,您不希望整个工作流停止,并且可能需要再次执行该活动集,直到达到一定的次数,看看它是否能正常工作(重试向 Web 服务发送相同的请求),如果它工作正常,那么它应该在不终止工作流的情况下执行工作流中的其余活动。
理解客户活动的生命周期
活动在其生命周期中有六个状态。这些状态是:**已初始化、执行中、取消中、已关闭、补偿中和故障中**。
在**已初始化**状态期间,已经为活动创建了 ActivityExecutionContext
,并且已经执行了该活动特定的其他初始化详细信息。当活动进入**执行中**状态时,活动的主要功能就会被执行。一个活动会被父活动显式地置于**取消中**状态,或者因为在该活动执行过程中抛出了异常。**已关闭**状态是活动的最后也是最终状态。唯一的例外是,如果一个活动成功完成,但根据业务逻辑必须经历补偿状态。然后,活动将从已关闭状态过渡到补偿状态,并在补偿逻辑完成后再次返回到已关闭状态。
下面的流程图显示了活动如何在各种活动状态之间转换。
所以,基本上,在正常路径下,活动会经历**已初始化、执行中**和**已关闭**。
研究的要点
我们知道,如果在活动的**执行中**、**取消中**或**补偿中**状态期间抛出了异常,它将过渡到**故障中**状态。
Note: An activity cannot move from the Closed state to the Executing state.
由于我们知道一旦活动关闭就无法再移动到执行中状态,但我们可以在活动关闭之前重新执行它。这意味着,如果由于任何原因活动进入了故障中状态,就有可能将其移回执行中状态。
现在,我们知道这里可能有一个新的转换。下图用绿色线条显示了新的状态转换。
我们知道,活动的所有状态都可以通过其预定义的方法进行控制。我们可以在自定义活动中重写这些方法。下面是我将在我的研究中使用的两个主要重写方法。
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
protected override ActivityExecutionStatus HandleFault(ActivityExecutionContext executionContext, Exception exception)
如何将活动状态从故障中状态恢复到执行中状态?
我们可以通过在活动内部处理故障来实现这一点。通过重写 HandleFault
方法来处理活动状态,我们可以知道 executionContext
是什么,抛出了哪种类型的异常,并且为了重新执行活动,我们可以调用 Execute
方法,并将 executionContext
传递给它。但是,在这样做时必须非常小心,并且要确保正确地处理重试逻辑,否则可能会导致死锁。
使用代码
在这里,我将通过我的示例 POC 项目向您展示我是如何实现的,您也可以使用相同的方法来实现。对您而言唯一不同的是您的重试逻辑/要求。我们总是需要确保重试逻辑不会陷入无限循环。
下面是我的项目解决方案。
在这个项目中,我有以下顺序工作流,包含三个活动。
在 CallWebServices 活动中,我试图模拟该活动正在调用 Web 服务,并且可能会出现服务器错误,这将抛出服务器错误故障异常,导致整个工作流因其抛出的异常而终止。
现在,为了解决整个工作流终止的问题,并从那里再次重试执行相同的活动,以便如果下次重试成功,并且从 Web 服务获得成功响应,工作流将继续完成编排。
Replaying => I am trying to achieve moving the activity state from Faulting to Executing
为了实现这个问题的解决方案,我添加了一个 BaseActivity,它将被我的所有工作流活动继承,如下所示。
public partial class Activity1: MyBaseActivity
我考虑编写基类活动,是因为根据我们解决方案的要求,我们需要每个活动都具备此重试功能。
我还向我的基类活动添加了一个依赖属性来设置/限制重试次数。因此,任何继承我的基类活动的活动都应该设置此属性。在下面的图片中,我为 CallWebServices 活动设置了 AllowedReplayCount 为 2,这意味着该活动最多可以重试自身 2 次。
以下是将依赖属性添加到我的基类活动的 [代码]
public static DependencyProperty AllowedReplayCountProperty =
DependencyProperty.Register("AllowedReplayCount", typeof(Int32), typeof(MyBaseActivity));
[DescriptionAttribute("AllowedReplayCount")]
[CategoryAttribute("Replay Category")]
[BrowsableAttribute(true)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
public Int32 AllowedReplayCount
{
get
{
return ((Int32)(base.GetValue(MyBaseActivity.AllowedReplayCountProperty)));
}
set
{
base.SetValue(MyBaseActivity.AllowedReplayCountProperty, value);
}
}
在这个基类活动中,我通过重写 HandleFault
函数来处理由活动抛出的故障。该函数提供了两个重要数据:executionContext
和 exception
。利用这些数据,我应用了重新执行相同活动的逻辑。并且通过依赖属性,我将控制重试,确保它不会陷入无限循环。
protected override ActivityExecutionStatus HandleFault(ActivityExecutionContext executionContext, Exception exception)
{
if (exception is WebException && ReplayCount < AllowedReplayCount)
{
if (!exception.Message.Contains("404") || !exception.Message.Contains("409"))
{
Console.WriteLine("Faulted " + executionContext.Activity.Name);
ReplayCount++;
return Execute(executionContext);
}
}
return base.HandleFault(executionContext, exception);
}
到目前为止,我们已经看到了如何通过 HandleFault 函数重试相同的活动,但这还不够,我们还需要工作流在故障活动重试成功后执行其余活动。我们需要这个,因为这是工作流的基本功能;如果任何活动发生故障,它会终止整个工作流。所以我们必须阻止工作流从执行中状态转移到故障中状态。
为了实现这种情况,我在我的基类活动中添加了一个 faultHandlerActivity。
为了支持重试,我为 faultHandlerActivity
设置了 FaultType 为 System.Net.WebException
。我还添加了 codeActivity
。
private void codeActivity1_ExecuteCode(object sender, EventArgs e)
{
CodeActivity senderActity = sender as CodeActivity;
FaultHandlerActivity faultException = senderActity.Parent as FaultHandlerActivity;
if (faultException.Fault is WebException)
{
if (ReplayCount > AllowedReplayCount)
{
throw faultException.Fault;
}
}
else
{
throw faultException.Fault;
}
}
我使用 codeAcitivity 来检查由活动抛出的故障是否符合重试条件。如果它不属于我的重试条件,那么我才允许它将故障抛出给工作流。
以上就是在一个工作流中实现此重试活动功能所需的一切。