一个大型交易应用的中央处理器尖峰故障排除


在本文中,我们将讨论如何解决北美一个主要交易应用程序中出现的CPU峰值问题。突然之间,这个应用程序的CPU开始飙升到100%。

这个团队没有进行任何新的代码部署,没有任何环境变化,他们没有翻转任何标志设置-但是突然之间CPU开始激增。我们甚至验证了交通量是否有所增加,这归因于交通量的激增。但是交通量也没有增加。

数据捕获

这个应用程序运行在Java、Tomcat技术堆栈上。我们请求站点可靠性工程(SRE)团队从发生此问题的计算机捕获以下两个工件:

  1. TOP-H输出
  2. 线程转储

让我们在这一节中看看这个工件包含什么。

1.TOP-H

CPU峰值总是由线程引起的。因此,我们必须隔离导致此CPU峰值的线程。这个应用程序有数百个线程。从这数百个线程中,我们需要确定哪些线程导致CPU消耗激增?这是第一个挑战。

这就是“顶级”Unix命令行实用工具派上用场的地方。你们中的大多数人可能都熟悉“顶级”Unix命令行实用程序。此工具显示设备上正在运行的所有进程。它还显示每个进程占用的CPU和内存。

这个工具有一个秘密的‘-H’选项:-),这是很多工程师都不熟悉的。可以这样调用:

$ top -H -p <PID>

其中PID是您的应用程序的进程ID。此应用程序的进程ID为31294。因此,SRE团队发布了这个命令。

$ top -H -p 31294

当使用‘-H’选项调用‘top’工具时,它将开始显示在该特定进程中运行的所有线程。它还将显示该进程中每个线程占用的CPU和内存量。以下是我们从该交易应用程序获得的输出:

图:top-H-p{PID}

正如您从“top-H”输出中看到的,在第一行中有一个id为‘11956’的线程。仅此线程就消耗了60.9%的CPU。答对了!!这是第一次获胜。现在,使用这个“top-H”选项,我们已经确定了消耗大量CPU的线程。

2.线程转储

我们的下一个挑战是识别这个“11956”线程正在执行的代码行。这就是线程转储派上用场的地方。线程转储显示应用程序中运行的所有线程及其代码执行路径(即堆栈跟踪)。

This blog突出显示了捕获线程转储的8个不同选项。您可以使用对您方便的选项。我们使用了“jstack”工具(它是JDK的一部分)来捕获线程转储。

最佳实践:在每个线程转储之间的10秒间隔内,至少捕获3个线程转储。

分析数据

现在,我们将“top-H”输出和“线程转储”都上载到fastThread工具。这个工具可以分析线程转储,‘top-H’输出,并生成直观的报告。该工具分析并生成this beautiful report

此报告包含“CPU|Memory”部分。这是该工具将top-H输出和线程转储结合在一起的部分,并提供应用程序中每个线程消耗的CPU和内存。

图:CPU,每个线程消耗的内存(由fast Thread生成)

在上面的屏幕截图中,我们可以看到第一行的“WebContainer:18”线程报告消耗了“60.9%”的CPU。在最后一列中,您可以看到该线程的代码执行路径(即堆栈跟踪)。您可以看到工具现在报告线程名称(即‘WebContainer:18’)和它正在执行的代码路径(当我们看到原始top-H输出时,它是不可用的)。

图:高CPU消耗线程的堆栈跟踪(由fast Thread生成)

分辨率

您可以注意到‘WebContainer:18’线程正在执行java.util.WeakHashMap#put()代码行。对于那些没有意识到的人来说,HashMap不是一个线程安全的实现。当多个线程并发调用HashMap的get()和put()方法时,可能会导致无限循环。

当线程无限循环时,CPU消耗将开始急剧增加。这正是此应用程序中出现的问题。一旦用ConcurrentHashMap替换了WeakHashMap,问题就解决了。

我们希望您会发现这个简单的技术很有用。