当 Perl 进程持续收到请求、CPU 使用率却始终上不去(本例中只有 13% 左右)时,说明有代码路径正在阻塞操作系统线程——通常是同步 I/O,比如卡在 select 上等待响应的 HTTP 调用。本文演示如何用 OpenResty XRay 的 off-CPU 火焰图,把阻塞的 Perl 代码路径精确定位到源文件和行号,全程无需修改代码、无需重启进程。

症状:请求涌入但 Perl 进程 CPU 使用率上不去

首先运行 top 命令检查 CPU 的使用情况。

可以看到这个名为 Perl 的进程。它的 CPU 只有 13% 左右。即使有很多请求进来,CPU 用量也不会上升。

top 命令输出显示 Perl 进程 CPU 使用率停在 13% 左右

让我们运行 ps 命令来查看这个进程的更多细节。

这里我们可以看到,它是 Linux 发行版自带的标准 perl 二进制可执行文件。

ps 命令输出显示这是 Linux 发行版自带的标准 perl 二进制文件

接下来我们来看一下这个 Perl 应用的访问日志。

可以看到,有很多客户端请求正在涌入,但是 CPU 使用率仍然很低。这意味着有一些东西阻塞了 Perl 代码高效运行。我们怎么才能找到原因呢?

Perl 应用的访问日志显示大量客户端请求正在涌入

访问日志持续增长而 CPU 使用率仍然很低

用 OpenResty XRay 的 off-CPU 火焰图定位阻塞的 Perl 代码路径

让我们使用 OpenResty XRay 来检查这个未经修改的进程。我们可以对它进行实时分析,并找出原因。

在浏览器中打开 OpenResty XRay 的 Web 控制台,确认当前选中的是正确的机器,然后进入 “Guided Analysis” 页面。

OpenResty XRay Web 控制台中的 Guided Analysis 页面

在系统能分析的各类问题中,选择 “Low CPU usage and cannot go up”。

在 Guided Analysis 中选择 “Low CPU usage and cannot go up” 问题类型

接着按向导操作:选择之前的 Perl 应用,选中消耗 14% CPU 资源的进程(也就是我们在 top 中看到的那个),其余步骤保持默认即可——应用类型、Perl 和 C/C++ 两个语言级别、以及 300 秒的最大分析时间。

在 Guided Analysis 向导中选择消耗 14% CPU 的 Perl 进程

开始分析。系统会持续执行多轮分析;对这个例子来说一轮就够了,我们就此停止。

Guided Analysis 正在对 Perl 进程执行第一轮分析

可以看到已经自动生成了一份分析报告。

自动生成的 Guided Analysis 分析报告

这是现在我们要分析的问题类型,off-CPU。

Guided Analysis 报告显示 off-CPU 问题类型

这是阻塞操作系统线程最严重的 C 代码路径。

阻塞操作系统线程最严重的 C 代码路径

第一个函数是 select,一个系统调用函数。

阻塞 C 代码路径顶部的 select 系统调用

Perl_pp_select 是处理 Perl 中 select 函数的内置函数。它是 Perl 内部的一部分,用于监视和等待 socket 以及其他文件上的 I/O 事件。

C 代码路径中处理 Perl select 函数的内置函数 Perl_pp_select

从这个 C 函数可以看到当前正在执行 Perl 代码。

该 C 函数栈帧显示当前正在执行 Perl 代码

接下来,我们看看阻塞最严重的 Perl 代码路径。

阻塞操作系统线程最严重的 Perl 代码路径

最顶部的 C 函数 select 正是我们刚才看到的 C 代码路径中的阻塞点。

Perl 代码路径的阻塞点 select 函数

Net::HTTP::Methods 模块的 can_read Perl 函数会等待直到 socket 接收到可读取的新数据。

Net::HTTP::Methods 模块的 can_read 函数在等待 socket 数据

沿着调用链,我们可以看到它在读取 read_response_headers 函数中的响应头时发生了阻塞。

read_response_headers 函数在读取 HTTP 响应头时发生阻塞

remote_fetch 是业务级别代码中的一个函数,它属于我们自己的 Perl 模块 Service::Processor

业务级 Perl 模块 Service::Processor 中的 remote_fetch 函数

最显著的阻塞性代码路径是从这个 Perl 语言级别的 off-CPU 火焰图中自动推导出来的。这样的 off-CPU 分析同样是诊断延迟问题的标准手段——可以看看我们如何在一个 500k QPS 的 OpenResty 网关中定位 244 毫秒的性能异常

Perl 语言级 off-CPU 火焰图标出最关键的阻塞代码路径

这里是对当前问题更详细的解释和建议。

报告中关于 off-CPU 问题的详细解释和建议

这里提到了我们前面看到的 select 函数。

报告文字提到 select 函数

这里提到 Net::HTTP::Methods::can_read 函数使用 select 调用根据数据进行下一步。

报告说明 can_read 函数使用 select 调用进行下一步

这也是我们之前看到的 remote_fetch 函数的引用。

报告中对 remote_fetch 函数的引用

让我们回到之前的热代码路径。

把鼠标放在名为 remote_fetch 的 Perl 函数的绿色框上。

鼠标悬停在火焰图中 remote_fetch 函数的绿色框上

可以看到这个函数的 Perl 源文件的完整路径。

提示框显示 remote_fetch 函数的源文件路径和行号

点击复制这个函数完整的 Perl 源文件路径。

从火焰图提示框复制 Perl 源文件路径

在终端上粘贴我们刚刚复制的代码路径,使用 vim 编辑器查看相应的 Perl 业务代码。您可以使用任何您喜欢的编辑器。

在终端中用 vim 编辑器打开 Perl 源文件

正如 OpenResty XRay 建议的那样跳转到第 66 行。

按 OpenResty XRay 的建议跳转到 Perl 源文件第 66 行

我们可以看到它正在发送 HTTP GET 请求并等待其响应。

Perl 代码正在发送 HTTP GET 请求并等待响应

为了避免在 Perl 中阻塞 HTTP 请求,您可以考虑使用像 Coro 这样的非阻塞框架。同样的 off-CPU 分析方法也适用于 GoPython 进程。

建议使用 Coro 等非阻塞 Perl 框架

Insights 页面中的全自动分析与报告

OpenResty XRay 也可以自动监控在线进程,并在 “Insights” 页面中生成以日和周为周期的分析报告——您不是非得手动使用 “Guided Analysis” 功能,当然它对应用的开发和演示仍然很有用。

OpenResty XRay Insights 页面中的日报和周报

如果你喜欢这个教程,请订阅这个博客网站和我们的 B 站频道。谢谢!

FAQ

为什么 Perl 进程在大量请求下 CPU 使用率还是上不去?

因为有代码路径在阻塞操作系统线程,而不是在 CPU 上做实际工作。在上面的案例中,Net::HTTP::Methods 模块的 can_read 函数卡在 select 系统调用上等待 socket 收到新数据,于是进程在请求不断堆积的同时基本处于空闲等待状态。

如何在不修改代码的情况下找到阻塞的 Perl 代码?

用 OpenResty XRay 的 “Guided Analysis” 直接分析未经修改的运行中进程,选择 “Low CPU usage and cannot go up” 问题类型。生成的报告会自动推断出最关键的阻塞代码路径,鼠标悬停在 off-CPU 火焰图的函数框上即可看到对应的源文件和行号。

本例中的 off-CPU 火焰图展示了什么?

它展示的是操作系统线程把时间耗在阻塞等待(而非 CPU 执行)上的代码路径。在本例中,Perl 语言级 off-CPU 火焰图直接指向业务函数 remote_fetch——它在 read_response_headers 中通过 select 调用等待 HTTP 响应时发生阻塞。

关于 OpenResty XRay

OpenResty XRay 是一个动态追踪产品,它可以自动分析运行中的应用,以解决性能问题、行为问题和安全漏洞,并提供可行的建议。在底层实现上,OpenResty XRay 由我们的 Y 语言驱动,可以在不同环境下支持多种不同的运行时,如 Stap+、eBPF+、GDB 和 ODB。

关于作者

章亦春是开源 OpenResty® 项目创始人兼 OpenResty Inc. 公司 CEO 和创始人。

章亦春(Github ID: agentzh),生于中国江苏,现定居美国湾区。他是中国早期开源技术和文化的倡导者和领军人物,曾供职于多家国际知名的高科技企业,如 Cloudflare、雅虎、阿里巴巴, 是 “边缘计算“、”动态追踪 “和 “机器编程 “的先驱,拥有超过 22 年的编程及 16 年的开源经验。作为拥有超过 4000 万全球域名用户的开源项目的领导者。他基于其 OpenResty® 开源项目打造的高科技企业 OpenResty Inc. 位于美国硅谷中心。其主打的两个产品 OpenResty XRay(利用动态追踪技术的非侵入式的故障剖析和排除工具)和 OpenResty Edge(最适合微服务和分布式流量的全能型网关软件),广受全球众多上市及大型企业青睐。在 OpenResty 以外,章亦春为多个开源项目贡献了累计超过百万行代码,其中包括,Linux 内核、Nginx、LuaJITGDBSystemTapLLVM、Perl 等,并编写过 60 多个开源软件库。

关注我们

如果您喜欢本文,欢迎关注我们 OpenResty Inc. 公司的博客网站 。也欢迎扫码关注我们的微信公众号:

我们的微信公众号

翻译

我们提供了英文版原文和中译版(本文)。我们也欢迎读者提供其他语言的翻译版本,只要是全文翻译不带省略,我们都将会考虑采用,非常感谢!