在 Linux 上调试 Go 服务的硬盘 I/O 高问题
在本教程中,OpenResty XRay 对一个名为 chat-service 的 Go 应用(基于 gin 框架)进行硬盘 I/O 高的问题诊断。通过 Go 级别的磁盘读写次数、延时和吞吐量火焰图,定位磁盘活动最密集的代码路径——本例中是 download.Handler 通过 http.serveFile 提供文件服务以及 gin 的请求日志——并精确到具体的源文件和行号。
定位硬盘 I/O 高的 Go 进程
在 OpenResty XRay 的 Dashboard 上,我们发现了一个名为 chat-service 的 Go 应用,它对硬盘进行了频繁的读写。
首先,运行 ps 命令来查看该应用。
我们可以看到完整的命令行。
使用火焰图追踪 Go 磁盘读写代码路径
使用 OpenResty XRay 来检查这个应用。我们可以对它进行实时分析,并找出原因。
在浏览器中打开 OpenResty XRay 的 Web 控制台。
确保当前分析的机器是正确的。
如果不对,我们可以在下面的列表重新选择。
进入 ”Guided Analysis“ 页面。
这里可以看到系统能分析的不同类型的问题。
选择 “High disk IO“。
点击 “Next”。
选择名为 chat-service 的 Go 应用。
选择我们之前在 ps 命令中看到的进程。
确保应用的类型是正确的。通常默认值就是对的。
这里的语言级别就只有 “Go” 了。
我们还可以设置最长的分析时间。这里保持默认的 300 秒不变。
开始分析。
系统将持续执行多轮分析。目前它正在运行第一轮分析。
第一轮分析已经完成,现在进入第二轮。对这个例子来说,运行一轮分析就够了。
停止分析。
这里显示系统正在为本次分析生成报告。
可以看到自动生成了一份分析报告。
这是我们要分析的问题类型,“Disk I/O”。
这是对硬盘读取次数的分析。
硬盘读取最多的是这条代码路径。
syscall.read 函数从文件描述符读取数据。
http.serveFile 函数将文件作为 HTTP 响应发送出去。
点击查看更多细节。
download.Handler 处理文件的下载。它属于业务函数。
handleHTTPRequest 是 gin 框架中的一个核心函数,它的主要作用是处理 HTTP 请求。gin 是一个用 Go 语言编写的高性能 HTTP Web 框架。
这条热代码路径是从这个 Go 级别的硬盘读取次数火焰图自动推导出来的。
下面是对问题更详细的解释和建议。
它提到了函数 download.Handler。
它是负责处理 HTTP 的请求和响应的。
也提到了 http.serveFile 函数。
它将文件作为 HTTP 响应发送出去。
让我们回到原始的代码路径。将鼠标停在这个函数的绿框上。
在提示框中可以看到这个函数的源文件名和完整路径。
这行源码的行号是 25。
点击这个图标,复制这个函数的源文件路径。
用 vim 编辑器打开源文件。粘贴我们刚才复制的文件路径。您可以使用任何您喜欢的编辑器。
正如 OpenResty XRay 建议的那样检查第 25 行。
这个函数将文件内容作为 HTTP 响应返回给客户端。我们建议改用 nginx 这样的 web 服务器代替 Go 应用来服务这些静态文件。
正如我们刚刚在报告中看到的,这行代码正是在 Handler 函数内部。
这是对硬盘读取延时的分析。
可以看到这条代码路径和我们刚才分析的代码路径是同一条,也是处理文件下载的。
它是硬盘读取延时的全部来源。
这是对硬盘读取吞吐量的分析。
也是那条处理文件下载的路径。
这条路径的读取速率接近 3 兆字节每秒。
这是对读取最多的文件的分析。
读取最多的文件是 188.jpg。
其读取速率达到了 417 KB 每秒。
这些是累积读取延时最长的文件。
延时最长的文件是 188.jpg。
延时占比为 36%。
这是对硬盘写操作次数的分析。
硬盘的写操作全部来源于这条代码路径。
这是 gin 包里用于记录请求访问日志的函数。
自动磁盘 I/O 分析报告
OpenResty XRay 也可以自动监控在线进程,并生成分析报告。
切换到 “Insights” 页面。
您可以在 “Insights” 页面中找到以日和周为周期的自动报告。
所以您不是非得用 “Guided Analysis” 功能。
当然,“Guided Analysis” 对于应用的开发和演示是很有用的。
关于 OpenResty XRay
OpenResty XRay 是一个动态追踪产品,它可以自动分析运行中的应用程序,以解决性能问题、行为问题和安全漏洞,并提供可行的建议。在底层实现上,OpenResty XRay 由我们的 Y 语言驱动,可以在不同环境下支持多种不同的运行时,如 Stap+、eBPF+、GDB 和 ODB。
常见问题
为什么我的 Go 服务硬盘 I/O 高?
在本例中,chat-service 的 download.Handler 调用 http.serveFile 直接从 Go 进程提供文件服务,这条代码路径是磁盘读延时的唯一来源;其中最繁忙的单个文件 188.jpg 占累计读延时的约 36%,读速率为 417 KB/s。
如何找到导致磁盘读取过多的 Go 函数?
运行 OpenResty XRay 的引导式分析,选择"High disk IO",然后选择目标 Go 进程,保持 Go 语言级别选中。它会生成磁盘读取次数火焰图,将鼠标悬停在热点函数上即可查看其精确的源文件和行号,直达 handler 内部的具体代码行。
Go 应用应该自己提供静态文件服务吗?
从性能和安全角度看不建议。在本例中,OpenResty XRay 追踪到磁盘读热点是一个 Go handler 直接提供静态图片文件,建议将静态文件服务交给 Nginx 等 Web 服务器,而不是在 Go 应用内部处理。
关于作者
章亦春是开源 OpenResty® 项目创始人兼 OpenResty Inc. 公司 CEO 和创始人。
章亦春(Github ID: agentzh),生于中国江苏,现定居美国湾区。他是中国早期开源技术和文化的倡导者和领军人物,曾供职于多家国际知名的高科技企业,如 Cloudflare、雅虎、阿里巴巴, 是 “边缘计算“、”动态追踪 “和 “机器编程 “的先驱,拥有超过 22 年的编程及 16 年的开源经验。作为拥有超过 4000 万全球域名用户的开源项目的领导者。他基于其 OpenResty® 开源项目打造的高科技企业 OpenResty Inc. 位于美国硅谷中心。其主打的两个产品 OpenResty XRay(利用动态追踪技术的非侵入式的故障剖析和排除工具)和 OpenResty Edge(最适合微服务和分布式流量的全能型网关软件),广受全球众多上市及大型企业青睐。在 OpenResty 以外,章亦春为多个开源项目贡献了累计超过百万行代码,其中包括,Linux 内核、Nginx、LuaJIT、GDB、SystemTap、LLVM、Perl 等,并编写过 60 多个开源软件库。
关注我们
如果您喜欢本文,欢迎关注我们 OpenResty Inc. 公司的博客网站 。也欢迎扫码关注我们的微信公众号:
翻译
我们提供了英文版原文和中译版(本文)。我们也欢迎读者提供其他语言的翻译版本,只要是全文翻译不带省略,我们都将会考虑采用,非常感谢!
















































































