我正在尝试实施Azure耐用功能工作流。
每隔6分钟,我就有一个Azure TimerTrigger函数调用一个Azure编排函数(OrchestrationTrigger),该函数又启动了许多活动函数(ActivityTrigger)。
有时候,编排功能有时会在几秒钟的时间内被调用两次!这是一个大问题,因为我的活动功能不是幂等的!
下面是我的代码被调用的方式。
TimerTriggered函数:
[FunctionName("StartupFunc")]
public static async Task Run([TimerTrigger("0 */6 * * * *", RunOnStartup = true, UseMonitor = false)]TimerInfo myStartTimer, [OrchestrationClient] DurableOrchestrationClient orchestrationClient, TraceWriter log)
{
List<OrchestrationModel> ExportModels = await getData();
string id = await orchestrationClient.StartNewAsync("OrchestratorFunc", ExportModels);
}
编排功能:
[FunctionName("OrchestratorFunc")]
public static async Task<string> TransformOrchestration([OrchestrationTrigger] DurableOrchestrationContext context, TraceWriter log)
{
var dataList = context.GetInput<List<OrchestrationModel>>();
var tasks = new List<Task>();
foreach (var data in dataList)
{
tasks.Add(context.CallActivityAsync<string>("TransformToSql", new TransformModel(data));
}
await Task.WhenAll(tasks);
}
活动功能:
[FunctionName("TransformToSql")]
[public static async Task<string> RunTransformation([ActivityTrigger] DurableActivityContext context, TraceWriter log)
{
TransformModel = context.GetInput<TransformModel>();
//Do some work with TransformModel
}
答案 0 :(得分:1)
这种行为非常好-持久功能是设计使然的。
您具有以下业务流程:
[FunctionName("OrchestratorFunc")]
public static async Task<string> TransformOrchestration([OrchestrationTrigger] DurableOrchestrationContext context, TraceWriter log)
{
var dataList = context.GetInput<List<OrchestrationModel>>();
var tasks = new List<Task>();
foreach (var data in dataList)
{
tasks.Add(context.CallActivityAsync<string>("TransformToSql", new TransformModel(data));
}
await Task.WhenAll(tasks);
}
调用活动时,流程返回到名为 Dispatcher 的概念-它是持久功能的内部实体,负责维护业务流程。等待任务完成之前,业务流程被临时释放。任务完成后,整个业务流程将重播,直到发生下一个await
。
这里很重要的一点是,尽管重播了业务流程,但活动不再被调用-它的结果是从存储中获取并使用的。使事情变得更加明确,请考虑以下示例:
[FunctionName("OrchestratorFunc")]
public static async Task<string> TransformOrchestration([OrchestrationTrigger] DurableOrchestrationContext context, TraceWriter log)
{
var dataList = context.GetInput<List<OrchestrationModel>>();
var tasks = new List<Task>();
foreach (var data in dataList)
{
await context.CallActivityAsync<string>("TransformToSql1", new TransformModel(data);
await context.CallActivityAsync<string>("TransformToSql2", new TransformModel(data);
}
}
等待TransformToSql1
时,业务流程将被释放,整个流程将等待直到该活动完成。然后,重新编排业务流程-它再次等待TransformToSql1
,但是由于保存了结果,因此它又回到业务流程并等待TransformToSql2
-然后重复该过程。
答案 1 :(得分:0)
随着持久功能框架的重播,编排功能将更频繁地运行。您是否看到活动功能也被再次触发?如果是这样,那的确是一种奇怪的行为。
耐用功能使用storage queues和表来控制编排的流程和捕获状态。
每次触发业务流程功能(通过从控制队列接收消息)时,它将重播业务流程。您可以使用DurableOrchestrationContext
上的IsReplaying
属性在代码中进行检查。
if (!context.IsReplaying)
{
// this code will only run during the first execution of the workflow
}
不要使用IsReplaying
来确定要运行的活动,而是将其用于记录/调试目的。
达到活动功能时,将一条消息放入工作项队列。然后,“耐用功能”框架将查看历史记录表,以确定此活动功能是否之前已经运行过(具有给定的参数)。如果是这样,则它将不会再次运行活动功能,它将继续执行业务流程的其余部分。
持久功能的检查点和重播功能仅在业务流程代码是确定性的时才起作用。因此,切勿使用基于DateTime或随机数的决策。
更多信息:https://docs.microsoft.com/en-us/azure/azure-functions/durable-functions-checkpointing-and-replay