持久功能 - 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。当您在本地 运行 持久函数时,这些会自动生成。
我们在应用程序服务中有一个长 运行 进程(可能需要超过 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。当您在本地 运行 持久函数时,这些会自动生成。