【译】一次交易类场景CPU飙升100%的故障排除案例转载
在本文中,我们将讨论如何解决北美主要交易应用程序中出现的 CPU 峰值问题。突然之间,这个应用程序的 CPU 开始飙升至 100%。事实上,这个团队没有进行任何新的代码部署,没有进行任何环境更改,也没有翻转任何标志设置——但突然间,CPU 开始飙升。我们甚至验证了流量是否增加,这归因于流量峰值。但交通量也没有增加。
数据采集
该应用程序运行在 Java、Tomcat 技术栈上。我们要求可靠性工程 (SRE) 团队从发生此问题的服务器中做了以下两步:
1. top-h 输出
2. Thread dumps
结果:
1.TOP-H
总是因为线程导致 CPU 峰值。所以我们必须隔离导致这个 CPU 峰值的线程。显然这个应用程序有数百个线程。从这数百个线程中,我们需要识别导致 CPU 消耗激增的线程?这是第一个挑战。
这就是'top' unix 命令行实用工具派上用场的地方。你们中的大多数人可能熟悉'top' unix 命令行实用程序。此工具显示设备上正在运行的所有进程。它还显示了每个进程消耗的 CPU 和内存。
这个工具有一个秘密的“-H”选项:-),很多工程师都不熟悉。可以像这样调用它:
$ top -H -p <PID>
其中 PID 是您的应用程序的进程 ID。显然这个应用程序的进程 ID 是 31294。因此 SRE 团队发出了这个命令。
$ top -H -p 31294
当使用此“-H”选项调用“top”工具时,它将开始显示在该特定进程中运行的所有线程。它还将显示该进程中每个线程消耗的 CPU 和内存量。下面是我们从这个交易应用程序中得到的输出:
图:顶部 -H -p {pid}
从“top -H”输出中可以看出,第一行有一个 id 为 '11956' 的线程。仅此线程就消耗了 60.9% 的 CPU。答对了!!这是第一场胜利。现在使用这个'top -H'选项,我们已经确定了消耗大量CPU的线程。
2. Thread dumps
我们的下一个挑战是识别这个“11956”线程正在执行的代码行。这是线程转储派上用场的地方。线程转储显示应用程序中运行的所有线程及其代码执行路径(即堆栈跟踪)。此博客重点介绍了 8 个不同的选项来捕获线程转储。您可以使用方便的选项。我们使用 JDK 中的“jstack”工具来捕获线程转储。
最佳实践:捕获至少 3 个线程转储,每个线程转储之间的间隔为 10 秒。
分析数据
现在我们将“top -H”输出和“线程转储”都上传到了fastThread工具。如果您不确定如何将“top -H”输出和线程转储上传到 fastThread 工具,这里是它的说明。该工具能够分析线程转储和“top -H”输出并生成直观的报告。该工具分析并生成了这份漂亮的报告。
此报告包含“CPU | 记忆”部分。这是该工具结合 top -H 输出和线程转储并提供应用程序中每个线程消耗的 CPU 和内存的部分。
图:CPU,每个线程消耗的内存(由 fastThread 生成)
在上面的屏幕截图中,我们可以看到第一行中的“WebContainer:18”线程报告正在消耗“60.9%”的 CPU。在最后一列,您可以看到该线程的代码执行路径(即堆栈跟踪)。您可以看到该工具现在报告线程名称(即“WebContainer:18”)和它正在执行的代码路径(当我们看到原始的 top -H 输出时,它不可用)。
图:高 CPU 消耗线程的 Stacktrace(由 fastThread 生成)
解决方案
您可以注意到“WebContainer: 18”线程正在执行 java.util.WeakHashMap#put() 代码行。对于不知道的人来说,HashMap 不是线程安全的实现。当多个线程同时调用 HashMap 的 get() 和 put() 方法时,可能会导致无限循环。当线程无限循环时,CPU 消耗将开始飙升。这就是这个应用程序中发生的确切问题。一旦 WeakHashMap 被 ConcurrentHashMap 替换,问题就解决了。
我们希望您发现这种简单的技术很有用。
原文地址:https://blog.fastthread.io/2020/04/23/troubleshooting-cpu-spike-in-a-major-trading-application/
原文作者:fastthread团队
原文链接:https://blog.fastthread.io/2020/04/23/troubleshooting-cpu-spike-in-a-major-trading-application/