在 Azure 应用服务平台上捕获内存转储

本文提供有关用于捕获内存转储的 Microsoft Azure 应用服务调试功能的指南。 使用的捕获方法取决于捕获内存转储以排查性能或可用性问题的方案。 例如,对于内存消耗过多的进程,捕获内存转储不同于引发异常或响应缓慢的进程。 此上下文中的进程是 Internet Information Services (IIS) 工作进程 (W3WP,它作为 w3wp.exe) 运行。

将内存转储方案映射到 Azure 应用服务调试功能

下表提供了有关每个应用服务功能运行以生成内存转储的命令的建议。 捕获内存转储的方法太多,导致进程可能令人困惑。 如果你已熟练捕获 W3WP 内存转储,则此信息不用于更改你的方法。 相反,我们希望为尚未开发首选项的缺乏经验的用户提供指导。

应用场景 Azure 应用服务调试功能 命令
无响应或速度缓慢 自动愈合 (请求持续时间) procdump -accepteula -r -dc "Message" -ma <PID> <PATH>
进程终止 (崩溃) 崩溃监视 使用 DbgHost 捕获内存转储
崩溃 (处理异常) Application Insights/Log Analytics 中的跟踪
崩溃 (未经处理的异常) Application Insights Snapshot Debugger
CPU 使用率过高 主动 CPU 监视 procdump -accepteula -dc "Message" -ma <PID> <PATH>
内存消耗过多 自动愈合 (内存限制) procdump -accepteula -r -dc "Message" -ma <PID> <PATH>

注意

我们有一个辅助建议,用于在无响应或慢速方案中捕获 W3WP 进程内存转储。 如果该方案可重现,并且你想要立即捕获转储,则可以使用 “收集内存转储” 诊断工具。 此工具位于 Azure 门户中给定应用服务 Web 应用的 “诊断和解决问题 ”工具集页。 另一个用于检查常规异常和性能不佳的位置位于 “应用程序事件日志 ”页上。 (还可以从 “诊断和解决问题 ”页访问应用程序日志。) 我们在 “扩展的 Azure 应用服务调试功能说明” 部分中讨论所有可用方法。

扩展的过程方案说明

本部分包含上表中所示的六种方案的详细说明。

无响应或慢速方案

向 Web 服务器发出请求时,通常必须运行某些代码。 代码执行发生在 线程上的w3wp.exe 进程中。 每个线程都有一个堆栈,显示当前正在运行的内容。

无响应方案可以是永久 (并且可能会) 超时或速度缓慢。 因此,无响应方案是请求运行时间超过预期的情况。 你可能会认为速度缓慢取决于代码正在执行的操作。 对于某些人来说,三秒的延迟很慢。 对于其他人,15 秒延迟是可以接受的。 基本上,如果看到指示速度缓慢的性能指标,或者超级用户指出服务器的响应速度比平常慢,则会出现无响应或速度缓慢的情况。

崩溃 (进程终止) 方案

多年来,Microsoft .NET Framework 改进了对异常的处理。 在当前版本的 .NET 中,异常处理体验甚至更好。

从历史上看,如果开发人员未将代码片段放在 try-catch 块中,并且引发异常,则进程将终止。 在这种情况下,开发人员代码中的未经处理的异常终止了进程。 .NET 的更多新式版本会处理其中一些“未经处理的”异常,以便运行代码的进程不会崩溃。 但是,并非所有未经处理的异常都是直接从自定义代码引发的。 例如,访问冲突 ((如0xC0000005和0x80070005) 或堆栈溢出)可能会终止进程。

故障 (处理) 方案

尽管软件开发人员需要特别注意确定代码运行的所有可能方案,但可能会出现意外情况。 以下错误可能会触发异常:

  • 意外的 null 值
  • 强制转换无效
  • 缺少实例化对象

最佳做法是将代码执行放入 try-catch 代码块中。 如果开发人员使用这些块,则代码有机会通过专门管理意外事件后的内容来正常失败。 处理的异常是在 try 块内引发并在相应的 catch 块中捕获的异常。 在这种情况下,开发人员预计可能发生异常,并围绕该代码部分编写了相应的 try-catch 块。

在 catch 块中,将足够的信息捕获到日志记录源中很有用,以便可以重现问题并最终解决问题。 在性能方面,异常是昂贵的代码路径。 因此,具有许多异常会影响性能。

故障 (未经处理的异常) 方案

当代码尝试执行预期不会遇到的操作时,会发生未经处理的异常。 与 崩溃 (进程终止) 方案一样,该代码不包含在 try-catch 代码块中。 在这种情况下,开发人员预计代码的该部分不会发生异常。

此方案不同于前两种异常方案。 在 崩溃 (未经处理的异常) 方案中,有关代码是开发人员编写的代码。 引发异常的不是框架代码,也不是终止 w3wp.exe 进程的未经处理的异常之一。 此外,由于引发异常的代码不在 try-catch 块中,因此无法正常处理异常。 最初,对代码进行故障排除有点复杂。 目标是查找异常文本、类型和堆栈,用于标识引发此未经处理的异常的方法。 该信息使你能够识别必须添加 try-catch 代码块的位置。 然后,开发人员可以添加类似的逻辑来记录 崩溃 (未经处理的异常) 方案中应存在的异常详细信息。

CPU 使用率过高方案

CPU 使用率过高是什么? 这种情况取决于代码的作用。 通常,如果 w3wp.exe 进程的 CPU 使用率为 80%,则应用程序处于可能导致各种症状的危急情况。 一些可能的症状包括:

  • 缓慢
  • 错误
  • 其他未定义的行为

如果网站只是传递静态 HTML 文件,即使 CPU 使用率达到 20%,也被视为过高。 通过生成内存转储对 CPU 峰值过高进行事后故障排除可能无助于确定使用它的特定方法。 你可以做的最好是确定哪些请求可能花费的时间最长,然后尝试通过测试标识的方法重现问题。 该过程假定你不会在捕获该突发的性能系统上运行性能监视器。 在许多情况下,可以通过让监视器持续实时运行来导致性能问题。

内存消耗过多方案

如果应用程序在 32 位进程中运行,内存消耗过多可能是个问题。 即使少量活动也会占用分配的 2-3 GB 虚拟地址空间。 无论可用的物理内存量如何,32 位进程的总大小都不能超过 4 GB。

64 位进程分配的内存比 32 位进程多。 与 64 位进程占用其分配的虚拟地址空间相比,64 位进程更有可能消耗服务器上的物理内存量。

因此,构成内存消耗过多的问题取决于以下因素:

  • 处理位 ( 32 位或 64 位)
  • 被视为“正常”的内存使用量。

如果进程消耗的内存超过预期,请收集内存转储进行分析,以确定哪些资源消耗了内存资源。 有关详细信息,请参阅 在应用服务消耗过多内存时创建内存转储

现在,你对内存转储可帮助你进行故障排除的不同进程方案有了更多的上下文,接下来我们将讨论在 Azure 应用服务平台上捕获内存转储的建议工具。

扩展的 Azure 应用服务调试功能说明

“将内存转储方案映射到 Azure 应用服务调试功能” 部分中的表中,我们确定了用于收集内存转储的六个调试功能。 选择“诊断工具”磁贴时,可从 Azure 门户的“诊断和解决问题”页上访问其中的每一项功能。

Azure 门户 Web 应用中的“诊断和解决问题”页和“诊断工具”磁贴的屏幕截图。

在以下各节中,我们将更详细地讨论其中的每一个调试功能。

自动愈合 (请求持续时间) 功能

如果响应完成时间超过预期,则 自动愈合 (请求持续时间) 功能对于捕获内存转储非常有用。 可以在上一屏幕截图的“诊断工具”磁贴中看到“自动愈合”链接。 选择该链接以直接转到该功能,或选择 “诊断工具” 磁贴以查看“ 诊断工具 ”页上的所有可用工具。 有关如何配置此功能的信息,请参阅以下文章:

以下屏幕截图显示了自动愈合功能。

Azure 门户的“自动愈合”页 (屏幕截图,其中包含诊断工具中) “请求持续时间”磁贴。

当问题当前发生或可重现时,另一个名为“收集内存转储”的功能在此方案中非常有用。 此功能可按需快速收集内存转储。

收集内存转储功能

若要了解“收集内存转储”功能的配置,请参阅 收集内存转储应用服务。 此方法需要手动干预。 以下屏幕截图显示了 “收集内存转储 ”页。

Azure 门户诊断工具中“收集内存转储”页的屏幕截图。

若要使用该功能,请选择存储内存转储的存储帐户。 然后,选择要从中收集内存转储的服务器实例。 如果有多个实例,请确保正在调试的问题正在该实例上发生。 请注意,在运行中的生产应用程序上,重启可能不是最佳选择。

崩溃监视功能

如果未经处理的异常导致 W3WP 进程终止,则崩溃监视功能可用于捕获内存转储。 以下屏幕截图显示了诊断工具中的“崩溃监视”页:

Azure 门户诊断工具中“崩溃监视”页的屏幕截图。

若要查看有关如何在 Azure 应用服务中配置崩溃监视功能的引导式演练,请参阅 Azure 应用服务中的崩溃监视

Application Insights/Log Analytics 功能中的跟踪

已处理的异常是尝试捕获块中包含的代码尝试执行意外或不受支持的操作的方案。 例如,以下代码片段尝试将数字除以零,即使这是非法操作:

decimal percentage = 0, number = 1000, total = 0;
try
{
  percentage = number / total;
}
catch (DivideByZeroException divEx)
{
  _logger.LogError("A handled exception just happened: -> {divEx.Message}", divEx.Message);
}

此代码片段会导致处理被零除的异常,因为不支持的数学运算放置在 try-catch 块中。 除非有意在应用程序代码中包含 Microsoft.ApplicationInsights NuGet 包,然后添加代码来记录信息,否则 Application Insights 不会记录处理的异常。 如果在添加代码后发生异常,则可以在 Log Analytics 中查看条目,如以下屏幕截图所示。

Azure 门户的 Application Insights/Log Analytics“日志”页中的跟踪屏幕截图。

以下 Kusto 代码包含用于从 Log Analytics 中提取数据的查询:

traces
| where message has "handled"
 | project timestamp, severityLevel, message, operation_Name, cloud_RoleInstance

message 是可以存储查找异常根本原因所需的详细信息的位置。 用于编写此查询的代码位于除以零的代码片段中。 编写此代码的软件开发人员是询问此类异常以及分析根本原因所需的属性的最佳人脉。

将此功能添加到应用程序代码的最佳方法取决于 (的应用程序代码堆栈和版本,例如,ASP.NET、ASP.NET Core、MVC、Razor 等) 。 若要确定方案的最佳方法,请查看 使用 .NET 的 Application Insights 日志记录

应用程序事件日志 (处理异常) 功能

还可以在 Azure 门户中诊断工具的“应用程序事件日志”页的“已处理异常”中找到未处理的异常,如以下屏幕截图所示。

Azure 门户的“诊断工具”“应用程序事件日志” (处理异常) 页的屏幕截图。

在这种情况下,你会收到通过代码记录的相同错误消息。 但是,在如何自定义 Application Insights 跟踪日志上的查询方面,将失去一些灵活性。

Application Insights Snapshot Debugger 功能

未处理的异常也会记录在 “应用程序事件日志 ”页上,如下一部分中的输出文本所示。 但是,还可以 启用 Application Insights Snapshot Debugger。 此方法不需要向应用程序添加任何代码。

应用程序事件日志 (未经处理的异常) 功能

以下输出来自 Azure 门户中诊断工具的“应用程序事件日志”页。 它显示了未经处理的应用程序异常的一些示例文本:

Category: Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware
EventId: 1
SpanId: 311d8cb5d10b1a6e
TraceId: 041929768411c12f1c3f1ccbc91f6751
ParentId: 0000000000000000
RequestId: 8000006d-0001-bf00-b63f-84710c7967bb
RequestPath: /Unhandled

An unhandled exception has occurred while executing the request.

Exception:
System.DivideByZeroException: Attempted to divide by zero.
   at System.Decimal.DecCalc.VarDecDiv(DecCalc& d1, DecCalc& d2)
   at System.Decimal.op_Division(Decimal d1, Decimal d2)
   at contosotest.Pages.Pages Unhandled.ExecuteAsync()
     in C:\Users\contoso\source\repos\contosorepo\contosorepo\Pages\Unhandled.cshtml:line 12

此处与应用程序日志中已处理的异常的一个区别在于存在用于标识方法的堆栈以及从中引发异常的行。 此外,可以放心地假设 Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware 功能包含用于捕获此未经处理的异常的代码,从而避免终止进程。 异常显示在“失败”页的“ 异常 ”选项卡上的 Application Insights ,如以下屏幕截图所示。

Azure 门户快照调试器的屏幕截图,位于 Application Insights 的“失败”页的“异常”选项卡上。

在此视图中,可以看到所有 异常,而不仅仅是要搜索的异常。 应用程序中发生的所有异常的图形表示形式有助于大致了解系统的运行状况。 与应用程序事件日志相比,Application Insights 仪表板在视觉上更有用。

主动 CPU 监视功能

在 CPU 使用率过高的情况下,可以使用主动 CPU 监视工具。 有关此工具的信息,请参阅 在 CPU 问题发生之前缓解这些问题。 下图显示了诊断工具中的“主动 CPU 监视”页。

Azure 门户诊断工具的“主动 CPU 监视”页的屏幕截图。

应将 CPU 使用率 80% 或更多视为需要立即调查的严重情况。 在 “主动 CPU 监视 ”页中,可以根据以下数据监视类别设置要为其捕获内存转储的方案:

  • CPU 阈值
  • 阈值秒
  • 监视频率

CPU 阈值 标识目标进程使用 (W3WP 的计算机 CPU 量,在这种情况下) 。 阈值秒 是在 CPU 阈值下使用 CPU 的时间量。 例如,如果总共 30 秒的 CPU 使用率为 75%,则会捕获内存转储。 如 “主动 CPU 监视 ”页上配置的那样,每隔 15 秒检查一次进程是否存在阈值违规。

自动愈合 (内存限制) 功能

如果进程消耗的内存超过预期,自动愈合 (内存限制) 功能对于捕获内存转储非常有用。 同样,请注意位 (32 或 64) 。 如果在 32 位进程上下文中遇到内存压力,并且预期内存消耗,则可以考虑将位数更改为 64。 通常,如果更改位数,还必须重新编译应用程序。

更改位数不会减少使用的内存量。 它允许进程使用超过 4 GB 的总内存。 但是,如果内存消耗量不符合预期,则可以使用此功能来确定占用内存的内容。 然后,可以执行操作来控制内存消耗。

“扩展的 Azure 应用服务调试功能说明”部分中,可以在第一个屏幕截图的“诊断工具”磁贴中看到“自动愈合”链接。 选择该链接以直接转到该功能,或选择磁贴并在 “诊断工具” 页中查看所有可用工具。 有关详细信息,请转到 Azure 应用服务诊断概述“自动修复”部分。

以下屏幕截图显示了自动愈合功能。

Azure 门户的“自动愈合”页 (屏幕截图,其中包含诊断工具中的“内存限制”磁贴) 。

选择“ 内存限制” 磁贴时,可以选择输入一个内存值,该值在超出内存限制时触发内存转储的捕获。 例如,如果输入 6291456 作为值,则当占用 6 GB 内存时,将采用 W3WP 进程的内存转储。

如果问题当前发生或可重现,则“收集内存转储”功能在此方案中非常有用。 此功能可按需快速收集内存转储。 有关详细信息,请参阅 “收集内存转储” 部分。

扩展的命令说明

记忆转储收集的艺术需要一些时间来研究、体验和完善。 如你了解的那样,不同的过程基于进程显示的症状,如 “扩展的过程方案说明” 部分中的表所列。 相比之下,下表将 Azure 应用服务的内存转储捕获命令与从 Kudu 控制台手动运行的 procdump 命令进行比较。

应用场景 Azure 应用服务命令 常规 procdump 命令
无响应或速度缓慢 procdump -accepteula -r -dc "Message" -ma <PID> <PATH> procdump -accepteula -ma -n 3 -s # <PID>
进程终止 (崩溃) 使用 DbgHost 捕获内存转储 procdump -accepteula -ma -t <PID>
崩溃 (处理异常) 无 (Application Insights) procdump -accepteula -ma -e 1 -f <filter> <PID>
崩溃 (未经处理的异常) 无 (Application Insights Snapshot Debugger) procdump -accepteula -ma -e <PID>
CPU 使用率过高 procdump -accepteula -dc "Message" -ma <PID> <PATH> procdump -accepteula -ma -n 3 -s # -c 80 <PID>
内存消耗过多 procdump -accepteula -r -dc "Message" -ma <PID> <PATH> procdump -accepteula -ma -m 2000 <PID>

在 Azure 应用服务中的内存转储捕获功能中使用的命令与手动捕获转储时使用的 procdump 命令不同。 如果查看上一部分,应注意到 Azure 应用服务中的内存转储收集门户功能公开了配置。 例如,在表中的内存消耗过多方案中,平台运行的命令不包含内存阈值。 但是,常规 procdump 命令列中显示的命令会指定内存阈值。

名为 DaaS (诊断即服务) 的工具负责管理和监视 Azure 应用服务调试门户中指定的配置。 此工具在运行 Web 应用的虚拟机 (VM) 上作为 Web 作业运行。 此工具的一个好处是,可以面向 Web 场中的特定 VM。 如果尝试直接使用 procdump 捕获内存转储,则在特定实例上识别、定位、访问和运行该命令可能具有挑战性。 有关 DaaS 的详细信息,请参阅 DaaS – Azure 网站的诊断即服务

CPU 使用率过高 是平台管理内存转储收集以便它们与建议的 procdump 模式匹配的另一个原因。 如上表所示,procdump 命令收集三个 (-n 3) 完整内存转储, (-ma) 相隔 30 秒 (-s #,其中 # CPU 使用率大于或等于 80% () 时为 30) -c 80 。 最后,向命令提供进程 ID (<PID>) : procdump -accepteula -ma -n 3 -s # -c 80 <PID>

可以在 “主动 CPU 监视” 部分查看门户配置。 为简洁起见,该部分仅显示了前三个配置选项: CPU 阈值 () -c 、阈值 (-s) 和 监视频率。 以下屏幕截图演示了 配置操作最大操作 (-n) 和 最长持续时间 是额外的可用功能。

诊断工具中扩展主动 CPU 监视的 Azure 门户屏幕截图。

学习捕获内存转储的不同方法后,下一步是练习进行捕获。 可以将 GitHub 上的代码示例与 IIS 调试实验室Azure Functions 结合使用,以模拟两个表中列出的每个方案。 将代码部署到 Azure 应用服务平台后,可以使用这些工具捕获每个给定方案下的内存转储。 经过一段时间和练习后,可以使用 Azure 应用服务调试功能完善捕获内存转储的方法。 以下列表包含继续了解内存转储收集时要考虑的一些建议:

  • 捕获内存转储会消耗大量系统资源,并进一步中断性能。

  • 第一次捕获内存转储不是最佳选择,因为可能会捕获太多内存转储。 这些第一机会内存转储很可能无关紧要。

  • 建议在捕获 W3WP 内存转储之前禁用 Application Insights。

收集内存转储后,下一步是分析内存转储以确定问题的原因,然后更正该问题。

后续步骤 (分析内存转储)

讨论如何分析内存转储不在本文的讨论范围内。 但是,该主题有许多资源,例如 Defrag Tools 培训系列和 必须知道的 WinDbg 命令列表

你可能已注意到上一屏幕截图中的 “配置操作” 选项。 此选项的默认设置为 CollectAndKill。 此设置意味着进程在收集内存转储后终止。 名为 CollectKillAndAnalyze 的设置分析收集的内存转储。 在这种情况下,平台分析可能会发现问题,因此你不必在 WinDbg 中打开内存转储并对其进行分析。

还有其他选项可用于排查和诊断 Azure 应用服务平台上的性能问题。 本文重点介绍内存转储收集,并针对使用这些方法进行诊断提供了一些建议。 如果你已经学习、体验和完善了收集过程,并且它们非常适合你,则应继续使用这些过程。

联系我们寻求帮助

如果你有任何疑问或需要帮助,请创建支持请求联系 Azure 社区支持。 还可以向 Azure 反馈社区提交产品反馈。