Durable Functions故障排除指南

本文可帮助你解决 Durable Functions 应用中的常见问题。 在以下列表中查找症状,并按照链接的步骤诊断并解决问题。

常见症状

有关可在 Application Insights 中运行的 KQL 诊断查询,请参阅 用于 Durable Functions 诊断的示例 KQL 查询

业务流程停滞在 Pending 状态

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

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

  1. 检查持久任务框架跟踪中是否存在有关受影响业务流程实例 ID 的警告或错误。 使用 Application Insights 中的 跟踪错误和警告查询 搜索与实例相关的错误。

  2. 检查 Azure 存储 控制队列,查看编排的“启动消息”是否仍在队列中。 在 Azure 门户中,导航到存储帐户,选择 Queues,然后查找具有 control 前缀的队列。 有关控制队列工作原理的背景信息,请参阅 Azure 存储 提供程序控制队列文档

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

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

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

  1. 检查延迟是否与Azure 存储 提供程序的已知限制匹配,例如分区重新均衡或基于计时器的轮询间隔。

  2. 检查持续任务框架跟踪中是否存在包含受影响业务流程实例 ID 的警告或错误。 使用 Application Insights 中的 跟踪错误和警告查询 搜索与实例相关的错误。

业务流程停滞在 Running 状态

如果编排状态显示“正在运行”的时间超过预期或者似乎进度已停止,则可能是编排在等待尚未完成的任务。 例如,它可能正在等待持久计时器、活动任务或外部事件。 如果计划的任务成功完成,但业务流程仍未推进,则可能存在问题,阻止其继续执行下一步。 处于此状态的业务流程通常称为“停滞业务流程”。

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

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

  2. 检查 Azure 存储帐户控制队列,查看是否有任何队列持续增长。 使用 Application Insights 中的 Azure 存储 消息查询来识别协调消息出队过程中的问题。 如果问题仅影响单个控制队列,则可能表示特定应用实例上存在问题。 在这种情况下,纵向扩展或缩减以移出不正常的 VM 实例可能会有所帮助。

  3. 按队列名称筛选Azure 存储消息查询结果,以查找与该特定控制队列分区相关的问题。

  4. 查看 Durable Functions 版本控制文档。 对在进行中的编排实例的重大更改可能会导致编排被卡住。

编排所需时间比预期要长

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

  1. 检查 Durable Task Framework 跟踪中的警告或错误,以便查看受影响的编排实例 ID。 使用 Application Insights 中的 跟踪错误和警告查询 搜索与实例相关的错误。

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

  3. 检查性能和可伸缩性瓶颈。 CPU 使用率高或内存消耗量大可能会导致延迟。 有关详细指导,请参阅 Durable Functions 中的性能和扩展

用于 Durable Functions 诊断的示例 KQL 查询

通过在为 Azure Functions 应用配置的 Azure 应用程序 Insights 实例中编写自定义 KQL 查询来排查问题。 有关这些查询中使用的列定义,请参阅 列引用

Azure 存储消息传送

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

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

以下查询检查特定业务流程实例的端到端Azure 存储交互。 编辑 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

Application Insights 列参考用于 Durable Functions 查询

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

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

消费计划中的连接管理的疑难

在Azure Functions消耗计划中运行的应用受连接限制的约束。 常见症状包括:

  • 调用活动函数或外部服务时出现间歇性连接错误。
  • 在高负载条件下偶尔失败的编排流程。
  • 日志中的套接字耗尽错误。

若要减少连接使用率,请使用 HttpClientFactory 或共享静态客户端,而不是在每个函数调用中创建新 HttpClient 实例。 有关连接池和最佳实践的详细指南,请参阅Azure Functions 中的管理连接

一般提示

Tip

在深入了解特定的故障排除步骤之前,请确保应用使用最新的Durable Functions扩展版本。 大多数情况下,使用最新版本可缓解其他用户已报告的已知问题。 有关如何升级的说明,请参阅 Upgrade Durable Functions 扩展版本

Azure门户中Diagnose 并解决问题选项卡可帮助监视和诊断与应用程序相关的问题并建议潜在解决方案。

获取对 Durable Functions 问题的支持

如果无法使用本指南解决问题,可以通过在 Azure 门户中的支持 + 故障排除部分打开新建支持请求(边栏)来提交支持请求。

 Azure门户中支持请求页面的截图。

如需提问或寻求社区支持,请在以下GitHub仓库之一中提交问题。 报告 bug 时,请包含受影响的实例 ID、UTC 中的时间范围、问题、应用程序名称(如果可能)和部署区域等信息,以加快调查速度。