在 Azure App 服务 平台上捕获内存转储
本文提供有关捕获内存转储的Microsoft Azure App 服务调试功能的指南。 使用的捕获方法取决于捕获内存转储以排查性能或可用性问题的情况。 例如,对于内存消耗过多的进程,捕获内存转储不同于引发异常或响应缓慢的进程。 此上下文中的进程是 Internet Information Services (IIS) 工作进程 (W3WP,它作为 w3wp.exe运行)。
将内存转储方案映射到Azure App 服务调试功能
下表提供有关每个App 服务功能运行以生成内存转储的命令的建议。 捕获内存转储的方法很多,进程可能会令人困惑。 如果你已经熟练地捕获 W3WP 内存转储,此信息并不打算更改你的方法。 相反,我们希望为尚未开发首选项的缺乏经验的用户提供指导。
场景 | Azure App 服务调试功能 | 命令 |
---|---|---|
无响应或速度缓慢 | 自动愈合 (请求持续时间) | procdump -accepteula -r -dc "Message" -ma <PID> <PATH> |
崩溃(进程终止) | 崩溃监视 | 使用 DbgHost 捕获内存转储 |
崩溃(处理异常) | Application Insights/Log Analytics 中的跟踪 | 无 |
崩溃(未经处理的异常) | Application Insights 快照调试器 | 无 |
CPU 使用率过高 | 主动 CPU 监视 | procdump -accepteula -dc "Message" -ma <PID> <PATH> |
内存消耗过多 | 自动愈合 (内存限制) | procdump -accepteula -r -dc "Message" -ma <PID> <PATH> |
注意
对于在无响应或缓慢方案中捕获 W3WP 进程内存转储,我们提供了辅助建议。 如果该方案可重现,并且想要立即捕获转储,则可以使用 “收集内存转储 ”诊断工具。 此工具位于Azure 门户中给定App 服务 Web 应用的诊断和解决问题工具集页面。 检查常规异常和性能不佳的另一个位置位于 “应用程序事件日志 ”页上。 (还可以从 访问应用程序日志诊断并解决问题页面。我们将讨论“扩展Azure App 服务调试功能说明”部分中的所有可用方法。
扩展的进程方案说明
本部分包含上表中所示的六种方案的详细说明。
无响应或慢速方案
向 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 文件,甚至可以将 20% 的 CPU 使用率视为过多。 通过生成内存转储对过度 CPU 峰值进行验尸故障排除可能无助于确定正在使用它的特定方法。 最好的方法是确定哪些请求可能需要最长的时间,然后通过测试已确定的方法尝试重现问题。 此过程假定你不会在捕获该突发的性能系统上运行性能监视器。 在许多情况下,可以通过让监视器持续实时运行来导致性能问题。
过多的内存消耗方案
如果应用程序在 32 位进程中运行,则内存消耗过多可能是个问题。 即使是少量的活动也能使用 2-3 GB 的已分配虚拟地址空间。 无论可用的物理内存量如何,32 位进程都不能超过 4 GB。
64 位进程分配的内存比 32 位进程多。 64 位进程使用服务器上的物理内存量比进程消耗其分配的虚拟地址空间的可能性更大。
因此,导致内存消耗过多的问题取决于以下因素:
- 处理位( 32 位或 64 位)
- 被视为“正常”的内存使用量。
如果进程消耗的内存超出预期,请收集内存转储进行分析,以确定消耗内存资源的内容。 有关详细信息,请参阅创建App 服务内存转储时消耗过多的内存。
现在,你对内存转储有助于排查的不同进程方案有了更多上下文,接下来我们将讨论用于在 Azure App 服务 平台上捕获内存转储的建议工具。
扩展的Azure App 服务调试功能说明
在“将内存转储方案映射到Azure App 服务调试功能”部分中的表中,我们发现了六项调试功能,这些功能旨在收集内存转储。 选择“诊断工具”磁贴时,可从“诊断”页面上的Azure 门户内访问这些功能并解决问题。
在以下部分中,我们将更详细地讨论其中每个调试功能。
自动愈合(请求持续时间)功能
如果响应花费的时间超过预期完成,则 自动愈合 (请求持续时间)功能可用于捕获内存转储。 可以在上一屏幕截图的“诊断工具”磁贴中看到“自动治愈”的链接。 选择该链接以直接转到该功能,或选择“诊断工具”磁贴以查看“诊断工具”页上的所有可用工具。 有关如何配置此功能的信息,请参阅以下文章:
以下屏幕截图显示了自动愈合功能。
当问题当前发生或可重现时,名为“收集内存转储”的另一项功能在此方案中非常有用。 此功能可按需快速收集内存转储。
收集内存转储功能
若要了解“收集内存转储”功能的配置,请参阅 “收集内存转储应用服务”。 此方法需要手动干预。 以下屏幕截图显示了“ 收集内存转储 ”页。
若要使用该功能,请选择用于存储内存转储的存储帐户。 然后,选择要从中收集内存转储的服务器实例。 如果有多个实例,请确保正在调试的问题发生在该实例上。 请注意,在运行中的生产应用程序上,重启可能不是最佳状态。
崩溃监视功能
如果未经处理的异常导致 W3WP 进程终止,则崩溃监视功能可用于捕获内存转储。 以下屏幕截图显示了诊断工具中的“崩溃监视”页:
若要查看有关如何在Azure App 服务中配置崩溃监视功能的引导式演练,请参阅Azure App 服务中的崩溃监视。
Application Insights/Log Analytics 功能中的跟踪
已处理的异常是一种方案,即 try-catch 块中包含的代码尝试执行意外或不受支持的操作。 例如,以下代码片段尝试将数字除以零,即使这是非法操作:
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 中查看条目,如以下屏幕截图所示。
以下 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 门户诊断工具的“应用程序事件日志”页的“已处理异常”中找到未经处理的异常,如以下屏幕截图所示。
在这种情况下,你会收到通过代码记录的相同错误消息。 但是,在如何自定义 Application Insights 跟踪日志上的查询方面,你失去了一些灵活性。
Application Insights Snapshot Debugger 功能
“应用程序事件日志”页上也会记录未经处理的异常,如下一部分中的输出文本所示。 但是,还可以 启用 Application Insights 快照调试器。 此方法不需要将任何代码添加到应用程序。
应用程序事件日志(未经处理的异常)功能
以下输出来自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 中,如以下屏幕截图所示。
在此视图中,你将看到所有 异常,而不仅仅是要搜索的异常。 应用程序中发生的所有异常的图形表示形式有助于大致了解系统的运行状况。 与应用程序事件日志相比,Application Insights 仪表板更有用。
主动 CPU 监视功能
在 CPU 使用率过高的情况下,可以使用主动 CPU 监视工具。 有关此工具的信息,请参阅 在 CPU 问题发生之前缓解 CPU 问题。 下图显示了诊断工具中的“主动 CPU 监视”页。
应将 CPU 使用率 80% 或更多视为需要立即调查的危急情况。 在 “主动 CPU 监视 ”页中,可以设置要基于以下数据监视类别捕获内存转储的方案:
- CPU 阈值
- 阈值秒
- 监视频率
CPU 阈值 标识目标进程使用的计算机 CPU 量(在本例中为 W3WP)。 阈值秒是在 CPU 阈值上使用 CPU 的时间量。 例如,如果总共 30 秒有 75% 的 CPU 使用率,则会捕获内存转储。 如在 “主动 CPU 监视 ”页上配置的那样,将每隔 15 秒检查一次阈值泄露。
自动愈合(内存限制)功能
如果进程消耗的内存超出预期,则自动愈合(内存限制)功能可用于捕获内存转储。 同样,请注意位(32 或 64)。 如果在 32 位进程上下文中遇到内存压力,并且预计内存消耗量,则可以考虑将位数更改为 64。 通常,如果更改位,则还必须重新编译应用程序。
更改位数不会减少使用的内存量。 它允许进程使用超过 4 GB 的总内存。 但是,如果内存消耗不如预期,则可以使用此功能来确定占用内存的内容。 然后,可以采取措施来控制内存消耗。
在“扩展的Azure App 服务调试功能说明”部分中,可以在第一个屏幕截图的“诊断工具”磁贴中看到自动治愈的链接。 选择该链接以直接转到该功能,或选择磁贴并查看“诊断工具”页中的所有 可用工具 。 有关详细信息,请转到Azure App 服务诊断概述的“自动修复”部分。
以下屏幕截图显示了自动愈合功能。
选择 “内存限制 ”磁贴时,可以选择输入一个内存值,该值在内存限制被破坏时触发内存转储的捕获。 例如,如果输入 6291456 作为值,则使用 6 GB 内存时,将使用 W3WP 进程的内存转储。
如果此问题当前发生或可重现,则“收集内存转储”功能在此方案中非常有用。 此功能可按需快速收集内存转储。 有关详细信息,请参阅 “收集内存转储” 部分。
展开的命令说明
记忆转储收藏艺术需要一些时间来学习、体验和完善。 如你了解的那样,不同的过程基于进程显示的症状,如“扩展进程方案说明”部分中的表中所列。 相比之下,下表将Azure App 服务的内存转储捕获命令与从 Kudu 控制台手动运行的 procdump 命令进行比较。
场景 | Azure App 服务 命令 | 常规 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 App 服务与手动捕获转储时使用的 procdump 命令不同。 如果查看上一部分,应注意到Azure App 服务中的内存转储收集门户功能公开配置。 例如,在表的内存消耗过多方案中,平台运行的命令不包含内存阈值。 但是,常规 procdump 命令列中显示的命令指定内存阈值。
名为 DaaS(诊断即服务)的工具负责管理和监视在Azure App 服务调试门户中指定的配置。 此工具在运行 Web 应用的虚拟机(VM)中作为 Web 作业运行。 此工具的一个好处是,可以在 Web 场中面向特定 VM。 如果尝试直接使用 procdump 捕获内存转储,则识别、目标、访问和在特定实例上运行该命令可能很困难。 有关 DaaS 的详细信息,请参阅 DaaS – 诊断即 Azure 网站的服务。
过多的 CPU 使用率 是平台管理内存转储收集以便与建议的 procdump 模式匹配的另一个原因。 procdump 命令如上表所示,在 CPU 使用率大于或等于 80% 时#
-s #
收集 30-n 3
秒(即 30 秒)的内存转储-ma
(-c 80
)。 最后,向命令procdump -accepteula -ma -n 3 -s # -c 80 <PID>
提供进程 ID (<PID>
) 。
可以在“主动 CPU 监视”部分查看门户配置。 为简洁起见,该部分仅显示前三个配置选项:CPU 阈值(-c
)、阈值秒(-s
)和监视器频率。 以下屏幕截图演示了 “配置操作”、“ 最大操作 ”-n
和 “最大持续时间 ”是额外的可用功能。
研究捕获内存转储的不同方法后,下一步是练习进行捕获。 可以将 GitHub 上的代码示例与 IIS 调试实验室和 Azure Functions 结合使用,以模拟两个表中列出的每个方案。 将代码部署到Azure App 服务平台后,可以使用这些工具在每个给定方案中捕获内存转储。 随着时间的推移和练习后,可以使用Azure App 服务调试功能来完善捕获内存转储的方法。 以下列表包含一些建议,建议继续了解内存转储收集:
捕获内存转储会消耗大量系统资源并进一步中断性能。
在第一次机会上捕获内存转储并不最佳,因为可能会捕获太多。 这些首次机会内存转储很可能无关紧要。
建议在捕获 W3WP 内存转储之前禁用 Application Insights。
收集内存转储后,下一步是分析内存转储以确定问题的原因,然后更正该问题。
后续步骤(分析内存转储)
讨论如何分析内存转储超出了本文的讨论范围。 但是,该主题有许多资源,例如 Defrag Tools 培训系列和 必须知道的 WinDbg 命令列表。
你可能已注意到 上一屏幕截图中的“配置操作 ”选项。 此选项的默认设置为 CollectAndKill。 此设置意味着收集内存转储后,进程将终止。 名为 CollectKillAndAnalyze 的设置分析收集的内存转储。 在这种情况下,平台分析可能会发现问题,这样就不必在 WinDbg 中打开内存转储并对其进行分析。
还有其他选项可用于排查和诊断 Azure App 服务 平台上的性能问题。 本文重点介绍内存转储收集,并提供有关使用这些方法进行诊断的一些建议。 如果你已经学习、体验和完善了你的收集过程,并且它们对你很出色,你应该继续使用这些过程。
联系我们寻求帮助
如果你有任何疑问或需要帮助,请创建支持请求或联系 Azure 社区支持。 你还可以将产品反馈提交到 Azure 反馈社区。