在复杂的工作流管理中,经常会遇到需要调用外部或内部Web服务的情况。如果这些服务因为内部服务器错误而抛出异常,可能会导致整个工作流的终止。本文将介绍一种解决方案,即使在活动失败的情况下,也能够重新执行活动,而不是让整个工作流终止。
在项目开发过程中,经常会遇到需要调用外部或内部Web服务的情况。如果这些服务因为内部服务器错误而抛出异常,可能会导致整个工作流的终止。为了解决这个问题,需要实现一种机制,使得在活动失败时能够重新执行该活动,而不是让整个工作流终止。
在工作流中,一个活动在其生命周期内可能会经历以下几种状态:初始化(Initialized
)、执行(Executing
)、取消(Canceling
)、关闭(Closed
)、补偿(Compensating
)和故障(Faulting
)。
在Initialized
状态下,为活动创建了ActivityExecutionContext
,并执行了特定于该活动的初始化细节。当活动进入Executing
状态时,执行该活动的主要功能。如果父活动明确地将活动置于Canceling
状态,或者在执行该活动时抛出异常,则活动将进入Canceling
状态。Closed
状态是活动的最后一个状态。
如果活动成功完成,但根据业务逻辑需要进入补偿状态,那么活动将从Closed
状态过渡到Compensating
状态,然后一旦补偿逻辑完成,再回到Closed
状态。
知道,如果在活动的Executing
状态、Canceling
状态或Compensating
状态中抛出异常,它将过渡到Faulting
状态。
注意:一旦活动进入Closed
状态,它就不能移动到Executing
状态。由于知道一旦活动关闭,它就不能移动到执行状态,但可以重新执行活动,然后它关闭。这意味着如果活动由于任何原因进入Faulting
状态,它可以返回到Executing
状态。
可以通过处理活动内部的故障来实现这一点。通过覆盖HandleFault
方法,可以知道什么是执行上下文以及抛出了什么类型的异常,并且可以通过调用Execute
方法来重新执行活动,传递执行上下文。但必须非常小心地做这件事,并且应该确保正在正确地处理重试逻辑,因为如果不这样做,可能会导致死锁。
在这里,将向展示示例POC项目是如何实现的,也可以使用相同的方法来实现它。
唯一不同的是重试逻辑/需求。总是需要确保重试逻辑不会进入无限循环。
在这个项目中,有一个顺序工作流,包含三个活动。在CallWebServices
活动中,试图模拟这个活动正在调用Web服务,并且可能会得到服务器错误,这将引发服务器错误故障异常,导致整个工作流终止。
现在,为了解决整个工作流终止的问题,并再次执行同一个活动,以便如果下一次重试成功并且从Web服务获得成功的响应,工作流应该继续完成编排。
为了实现这个问题的解决方案,添加了一个BaseActivity
,它将被所有的工作流活动继承。
还为基活动添加了一个依赖属性,以设置/限制重试次数。因此,任何继承基活动的活动都应该设置这个属性。在下面的图片中,将AllowedReplayCount
设置为CallWebServices
活动为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
函数来处理活动抛出的故障。这个函数给提供了两个重要的数据:执行上下文和异常。使用这些数据,应用了重试逻辑。
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
函数来重试同一个活动,但这还不是全部,还需要工作流在重试成功之后执行其他活动。这是因为它的基本特性是,如果任何活动出现故障,它将终止整个工作流。所以需要阻止工作流从执行状态进入故障状态。
为了实现这种情况,在基活动中添加了faultHandlerAcitivity
。为了支持重试,将faultHandlerActivity
的故障类型设置为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;
}
}