持久功能 - Activity 功能在空闲后重新启动?

Durable Functions - Activity Function Restarted After Idle?

我们在应用程序服务中有一个长 运行 进程(可能需要超过 5 分钟),由于 Azure 的内置负载平衡器 non-configurable 230-second timeout 而超时。

因此我们使用 Azure Durable Functions 重构 async-http API pattern。我们有一个单一的 activity 功能,由于超出本问题范围的原因,它不能轻易分解为更小的工作。

我注意到输出日志中有奇怪的结果,并确定 activity 函数在几分钟后被 Azure Functions 重新启动。我在 activity 函数中设置了一个断点,它在几分钟后(再次)被命中。

这不是我自己配置​​的;我启动函数的调用代码只执行一次。这是怎么回事?如何使 activity 函数 运行 完成?

当工作量不足几分钟时,它工作正常并按预期完成。

函数应用程序代码如下所示:

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
using OurContentModel;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.DurableTask;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

namespace Content20.Store
{
    public class StoreContent
    {
        /// <summary>
        /// Starter function called by HTTP. Starts the orchestrator and returns an endpoint the client
        /// can query for status and for the result once complete.
        /// </summary>
        /// <remarks>See https://docs.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-overview?tabs=csharp#async-http </remarks>
        [FunctionName("StoreContent")]
        public async Task<IActionResult> HttpStart(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")]
            HttpRequest req,
            [DurableClient] IDurableOrchestrationClient starter,
            ILogger log)
        {
            // Get function input comes from the request content and query params.
            // ...
            
            var content = JsonConvert.DeserializeObject<OurData>(requestBody);
            string instanceId = await starter.StartNewAsync(
                "StoreContent_RunOrchestrator",
                new StoreContentInputArgs()
                {
                    OurContent = content
                });

            log.LogInformation($"Started orchestration with ID = '{instanceId}'.");

            return starter.CreateCheckStatusResponse(req, instanceId);
        }

        /// <summary>
        /// Orchestration function that calls the activity function(s)
        /// and returns the final result when they're done.
        /// </summary>
        [FunctionName("StoreContent_RunOrchestrator")]
        public async Task<StoreContentResult> RunOrchestrator(
            [OrchestrationTrigger] IDurableOrchestrationContext context)
        {
            var input = context.GetInput<StoreContentInputArgs>();
            return await context.CallActivityAsync<StoreContentResult>("StoreContent_WriteFamilyData", input);
        }

        /// <summary>
        /// Activity function that does the actual work.
        /// </summary>
        [FunctionName("StoreContent_WriteFamilyData")]
        public async Task<StoreContentResult> WriteFamilyData([ActivityTrigger] StoreContentInputArgs input, ILogger log)
        {
            try
            {
                // breakpoint here gets hit a second time when first invocation takes more than a few minutes, 
                // with "external code" below it in the call stack so I assume it's getting (re)started by the system?
                var storer = new OurContentStorer(log);
                await storer.StoreContentAsync(input); // long-running process
                return new StoreContentResult()
                {
                    Success = true,
                    Message = "OK"
                };
            }
            catch (Exception ex)
            {
                log.LogError(ex, ex.ToString());
                return new StoreContentResult()
                {
                    Success = false,
                    Message = ex.Message
                };
            }
        }
    }
}

我们已经在 host.json 中将函数的超时时间增加到一个小时。 运行 Azure 的高级计划。

当我在 activity 函数中设置中断时,第二次命中断点时调用堆栈如下所示:

事实证明,“重新启动”实际上只是剩余的调用。在某些时候,我终止了该进程(这都是使用 Azure Functions Tools 进行的所有本地调试),它在我的 Azure 存储模拟器中留下了一些状态。所以我本地的 Azure 工具认为持久化功能仍然 运行ning and/or 需要重新启动。

为了摆脱僵尸调用,我刚刚使用 Azure Storage Explorer 从我的模拟器中删除了所有“testhub”表、队列和 blob。当您在本地 运行 持久函数时,这些会自动生成。