Azure 持久函数 - CallActivityAsync 时的 InvalidOperationException

zav*_*kas 8 c# azure azure-functions azure-durable-functions

我正在尝试 Azure Durable 功能。目前,我在InvalidOperationException调用活动后进入编排功能。它抱怨检测到多线程执行。如果协调器功能之前从不受支持的异步回调中恢复,则可能会发生这种情况

有没有人遇到过这样的问题?我做错了什么?完整代码可以在GitHub找到

这是编排函数中的一行:

var res = await ctx.CallActivityAsync<int>("LengthCheck", "inputData");
Run Code Online (Sandbox Code Playgroud)

LengthCheckactivitiy功能是:

[FunctionName("LengthCheck")]
public static Task<int> Calc([ActivityTrigger] string input)
{
    var task = Task.Delay(TimeSpan.FromSeconds(5));
    task.Wait();
    return Task.FromResult(input.Length);
}
Run Code Online (Sandbox Code Playgroud)

堆栈跟踪是:

ac6fd5cdd07a4dc9b2577657d65c4f27:函数“InpaintOrchestration(Orchestrator)”,版本“”失败并出现错误。原因:System.InvalidOperationException:检测到多线程执行。如果协调器功能之前从不受支持的异步回调中恢复,则可能会发生这种情况。

在 Microsoft.Azure.WebJobs.DurableOrchestrationContext.ThrowIfInvalidAccess()

在 Microsoft.Azure.WebJobs.DurableOrchestrationContext.d__47`1.MoveNext()

从上一个抛出异常的位置开始的堆栈跟踪结束

在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)

在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)

在 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()

Chr*_*lum 6

每当协调器功能以不受支持的方式执行异步工作时,就会发生此异常。在此上下文中,“不受支持”实际上意味着await用于非持久任务(而“非持久”意味着它是来自除 之外的某些 API 的任务DurableOrchestrationContext)。

您可以在此处找到有关协调器功能的代码约束的更多信息:https : //docs.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-checkpointing-and-replay#orchestrator-code-约束

以下是我快速扫描您的代码时违反的规则:

  • Orchestrator 代码应该是非阻塞的。例如,这意味着没有 I/O,也没有对 Thread.Sleep 或等效 API 的调用。如果协调器需要延迟,它可以使用CreateTimer API。

  • Orchestrator 代码绝不能启动任何异步操作,除非使用DurableOrchestrationContext API。例如,没有 Task.Run、Task.Delay 或 HttpClient.SendAsync。Durable Task Framework 在单个线程上执行协调器代码,并且无法与其他异步 API 可以调度的任何其他线程交互。

当我们检测到进行了不受支持的异步调用时,会特别发生此异常。我注意到这段代码中发生了这种情况:

    private static async Task SaveImageLabToBlob(ZsImage imageLab, CloudBlobContainer container, string fileName)
    {
        var argbImage = imageLab
            .Clone()
            .FromLabToRgb()
            .FromRgbToArgb(Area2D.Create(0, 0, imageLab.Width, imageLab.Height));

        using (var bitmap = argbImage.FromArgbToBitmap())
        using (var outputStream = new MemoryStream())
        {
            // modify image
            bitmap.Save(outputStream, ImageFormat.Png);

            // save the result back
            outputStream.Position = 0;
            var resultImageBlob = container.GetBlockBlobReference(fileName);
            await resultImageBlob.UploadFromStreamAsync(outputStream);
        }
    }
Run Code Online (Sandbox Code Playgroud)

进行异步或阻塞调用的正确方法是将它们包装在没有任何这些约束的活动函数中。

在此扩展的更新版本(v1.3.2 及更高版本)中,我们包含了一个链接,指向描述异常消息中的代码约束的文档。