다음을 통해 공유

Durable Functions故障排除指南

Durable Functions 是 Azure Functions 的扩展,可用于使用普通代码构建无服务器编排。 有关Durable Functions的详细信息,请参阅 Durable Functions 概述

本文可帮助你排除故障Durable Functions应用程序中常见的问题。

注释

Microsoft支持工程师可以帮助诊断您的应用程序问题。 如果无法使用本指南诊断问题,请在 Azure 门户中打开函数应用页面的 支持与疑难解答 部分,点击 新建支持请求 边栏选项卡来提交支持请求。

⟨c1⟩⟨c0⟩⟨sb0⟩Azure 门户中支持请求页的截图。⟨/sb0⟩⟨/c0⟩⟨/c1⟩

小窍门

调试和诊断问题时,建议首先确保应用使用最新的Durable Functions扩展版本。 大多数情况下,使用最新版本可缓解其他用户已报告的已知问题。 有关如何升级扩展版本的说明,请参阅 Upgrade Durable Functions 扩展版本

Azure 门户中的 “诊断和解决问题 ”选项卡是一种有用的资源,用于监视和诊断与应用程序相关的可能问题。 它还根据诊断提供问题的潜在解决方案。 有关详细信息,请参阅 Azure 函数应用诊断

如果以前的资源无法解决问题,以下部分提供有关特定应用程序症状的建议:

业务流程停滞在 Pending 状态

启动一个协调流程时,会将“start”消息写入由 Durable 扩展管理的内部队列,协调流程的状态设置为“等待中”。 在一个可用的应用实例接收并成功处理编排消息后,状态将转换为“正在运行”或其他非“挂起”状态。

按照以下步骤对长时间停留在“待处理”状态的编排实例进行故障排除。

  • 检查持久任务框架跟踪中是否存在有关受影响业务流程实例 ID 的警告或错误。 在 “跟踪错误/警告”部分查找示例查询。

  • 检查与停滞编排器关联的 Azure Storage 控制队列,以查看其“启动消息”是否仍然存在。 有关控制队列的详细信息,请参阅 Azure Storage 提供程序控制队列文档

  • 将应用的 平台配置 版本更改为“64 位”。 有时编排不会启动,因为应用程序内存不足。 切换到 64 位进程可让应用分配更多总内存。 此更改仅适用于应用服务基本、标准、高级和弹性高级计划。 免费或消耗计划 不支持 64 位进程。

业务流程在长时间延迟后启动

通常,编排任务在计划后几秒钟内开始。 在某些情况下,编排可能需要更长的时间才能启动。 请按照以下步骤排查当协调流程需要多于几秒钟才能开始执行时的问题。

  • 请参阅关于 Azure Storage 延迟协调的文档,以检查延迟是否由已知限制引起。

  • 检查持续任务框架跟踪中是否存在包含受影响业务流程实例 ID 的警告或错误。 在 “跟踪错误/警告”部分查找示例查询。

编排未完成或停滞在 Running 状态

如果业务流程长时间保持“正在运行”状态,则通常意味着它正在等待计划完成的长期运行任务。 例如,它可能正在等待一个持久计时器任务、一个活动任务或一个外部事件任务的完成。 如果观察到已计划的任务已成功完成,但业务流程仍未取得进展,则可能会导致一个问题阻止业务流程继续执行下一个任务。 处于此状态的业务流程通常称为“停滞业务流程”。

按照以下步骤排查停滞的业务流程问题:

  • 请尝试重启函数应用。 如果业务流程因应用或扩展代码中的暂时性 bug 或死锁而停滞,此步骤会有所帮助。

  • 检查 Azure 存储帐户控制队列,查看是否有任何队列持续增长。 此 Azure 存储消息传送 KQL 查询 可帮助识别出队编排消息的问题。 如果问题仅影响单个控制队列,则可能表示问题仅存在于特定应用实例上,在这种情况下,纵向扩展或缩减以移出运行不正常的 VM 实例可能会有所帮助。

  • Azure 存储消息部分 中使用 Application Insights 查询,以该队列名称作为分区 ID 进行筛选,并查找与该控制队列分区相关的任何问题。

  • 查看 Durable Functions 版本控制文档。 某些问题可能是由于正在进行的业务流程实例发生中断性变更造成的。

业务流程运行速度缓慢

大量数据处理、内部错误和计算资源不足可能会导致业务流程运行速度低于正常。 按照以下步骤对运行时间超过预期时间的业务流程进行故障排除:

  • 检查 Durable Task Framework 跟踪中的警告或错误,以便查看受影响的编排实例 ID。 可以在 “跟踪错误/警告”部分找到示例查询。

  • 如果应用使用 .NET 进程内模型,请考虑启用 扩展会话。 扩展会话可将历史记录负载降到最低,这可能会降低处理速度。

  • 检查性能和可伸缩性瓶颈。 应用程序性能取决于许多因素。 例如,CPU 使用率较高或内存消耗大可能会导致延迟。 有关详细指导,请参阅 Durable Functions 中的性能和扩展

示例查询

通过在为 Azure Functions 应用配置的 Azure Application Insights 实例中编写自定义 KQL 查询来排查问题。

Azure Storage消息传送

使用默认的 Azure Storage 提供程序时,所有 Durable Functions 行为都由 Azure Storage 队列消息驱动,与编排相关的所有状态都存储在表存储和 Blob 存储中。 启用 Durable Task Framework 跟踪时,所有Azure Storage交互都会记录到 Application Insights。 此数据对于调试执行和性能问题至关重要。

从 Durable Functions 扩展的 v2.3.0 开始,可以通过更新 host.json 文件中的日志记录配置,将这些 Durable Task Framework 日志发布到 Application Insights 实例。 有关详细信息,请参阅 Durable Task Framework 日志记录文章

以下查询检查特定业务流程实例的端到端Azure Storage交互。 编辑 startorchestrationInstanceID 按时间范围和实例 ID 进行筛选。

let start = datetime(XXXX-XX-XXTXX:XX:XX); // edit this 
let orchestrationInstanceID = "XXXXXXX"; //edit this
traces  
| where timestamp > start and timestamp < start + 1h 
| where customDimensions.Category == "DurableTask.AzureStorage" 
| extend taskName = customDimensions["EventName"]
| extend eventType = customDimensions["prop__EventType"] 
| extend extendedSession = customDimensions["prop__IsExtendedSession"]
| extend account = customDimensions["prop__Account"] 
| extend details = customDimensions["prop__Details"] 
| extend instanceId = customDimensions["prop__InstanceId"] 
| extend messageId = customDimensions["prop__MessageId"] 
| extend executionId = customDimensions["prop__ExecutionId"] 
| extend age = customDimensions["prop__Age"] 
| extend latencyMs = customDimensions["prop__LatencyMs"] 
| extend dequeueCount = customDimensions["prop__DequeueCount"] 
| extend partitionId = customDimensions["prop__PartitionId"] 
| extend eventCount = customDimensions["prop__TotalEventCount"] 
| extend taskHub = customDimensions["prop__TaskHub"] 
| extend pid = customDimensions["ProcessId"]
| extend appName = cloud_RoleName
| extend newEvents = customDimensions["prop__NewEvents"]
| where instanceId == orchestrationInstanceID
| sort by timestamp asc
| project timestamp, appName, severityLevel, pid, taskName, eventType, message, details, messageId, partitionId, instanceId, executionId, age, latencyMs, dequeueCount, eventCount, newEvents, taskHub, account, extendedSession, sdkVersion

跟踪错误和警告

以下查询搜索给定编排实例的错误和警告。 为 orchestrationInstanceID 提供值。

let orchestrationInstanceID = "XXXXXX"; // edit this
let start = datetime(XXXX-XX-XXTXX:XX:XX); 
traces  
| where timestamp > start and timestamp < start + 1h
| extend instanceId = iif(isnull(customDimensions["prop__InstanceId"] ) , customDimensions["prop__instanceId"], customDimensions["prop__InstanceId"] ) 
| extend logLevel = customDimensions["LogLevel"]
| extend functionName = customDimensions["prop__functionName"]
| extend status = customDimensions["prop__status"]
| extend details = customDimensions["prop__Details"] 
| extend reason = customDimensions["prop__reason"]
| where severityLevel >= 1 // to see all logs of severity level "Information" or greater.
| where instanceId == orchestrationInstanceID
| sort by timestamp asc 

控制队列和分区 ID 日志

以下查询搜索与 instanceId 的控制队列关联的所有活动。 提供实例 ID 的值 orchestrationInstanceID 以及查询的开始时间 start

let orchestrationInstanceID = "XXXXXX"; // edit this
let start = datetime(XXXX-XX-XXTXX:XX:XX); // edit this
traces  // determine control queue for this orchestrator
| where timestamp > start and timestamp < start + 1h 
| extend instanceId = customDimensions["prop__TargetInstanceId"] 
| extend partitionId = tostring(customDimensions["prop__PartitionId"])
| where partitionId contains "control" 
| where instanceId == orchestrationInstanceID
| join kind = rightsemi(
traces  
| where timestamp > start and timestamp < start + 1h 
| where customDimensions.Category == "DurableTask.AzureStorage" 
| extend taskName = customDimensions["EventName"]
| extend eventType = customDimensions["prop__EventType"] 
| extend extendedSession = customDimensions["prop__IsExtendedSession"]
| extend account = customDimensions["prop__Account"] 
| extend details = customDimensions["prop__Details"] 
| extend instanceId = customDimensions["prop__InstanceId"] 
| extend messageId = customDimensions["prop__MessageId"] 
| extend executionId = customDimensions["prop__ExecutionId"] 
| extend age = customDimensions["prop__Age"] 
| extend latencyMs = customDimensions["prop__LatencyMs"] 
| extend dequeueCount = customDimensions["prop__DequeueCount"] 
| extend partitionId = tostring(customDimensions["prop__PartitionId"])
| extend eventCount = customDimensions["prop__TotalEventCount"] 
| extend taskHub = customDimensions["prop__TaskHub"] 
| extend pid = customDimensions["ProcessId"]
| extend appName = cloud_RoleName
| extend newEvents = customDimensions["prop__NewEvents"]
) on partitionId
| sort by timestamp asc
| project timestamp, appName, severityLevel, pid, taskName, eventType, message, details, messageId, partitionId, instanceId, executionId, age, latencyMs, dequeueCount, eventCount, newEvents, taskHub, account, extendedSession, sdkVersion

应用洞察列参考文献

下表列出了上述查询结果中的列及其描述。

DESCRIPTION
pid 函数应用实例的进程 ID。 此值对于检查在运行业务流程时是否回收了进程会非常有用。
任务名称 要记录的事件的名称。
事件类型 这是一类消息,通常表示编排器完成的工作。 有关可能值及其说明的完整列表,请参阅 EventType.cs
扩展会话 指示布尔值是否启用了扩展会话
帐户 应用使用的存储帐户。
详情 有关特定事件的其他信息(如果有)。
instanceId 给定业务流程或实体实例的 ID。
消息ID 给定队列消息的唯一 Azure 存储 ID。 此值最常出现在 ReceivedMessage、ProcessingMessage 和 DeletingMessage 跟踪事件中。 由于消息 ID 是在消息发送后由 Azure Storage 生成的,因此 SendingMessage 事件中不存在此值。
执行ID 协调器执行的 ID,每当 continue-as-new 被调用时都会更改为新的 ID。
年龄 消息排队以来的毫秒数。 大量数字通常表示性能问题。 一个例外是 TimerFired 消息类型,该类型可能具有较大的 Age 值,具体取决于计时器的持续时间。
延迟毫秒 某些存储操作所耗费的毫秒数。
dequeueCount 取消排队消息的次数。 在正常情况下,此值始终为 1。 如果有多个,可能会出现问题。
分区标识ID 与此日志关联的队列的名称。
总事件计数 当前操作中涉及的历史事件个数。
taskHub 任务中心的名称。
新事件 正在写入存储中的历史表的以逗号分隔的历史事件列表。

消耗计划中的连接管理问题

在Azure Functions消耗计划上运行的应用受连接限制的约束。 如果遇到因连接耗尽而导致的间歇性连接错误或编排失败,请参阅 Azure Functions 中的 连接管理,了解连接池的指南以及减少连接使用的最佳做法。

获取支持

有关问题和支持,请在以下GitHub存储库之一中提出问题。 在Azure中报告 bug 时,请包含受影响的实例 ID、UTC 时间范围等信息,其中显示了问题、应用程序名称(如果可能)和部署区域,以大大加快调查速度。