中文站

云原生应用架构实践-应用健康检查

性能问题诊断

在性能诊断之前,我们要先清楚如何判定,或者说如何确定应用有性能问题,否则无法定位性能问题。总的来说,性能指标主要有以下两点。

吞吐量:每秒可以处理的请求数据或者任务数据。

响应时间:处理一个请求/任务的时间或者延迟。

整个系统的性能基本由这两个指标来反映,系统对性能指标可能有不同的偏好,在有 些场景下,系统可能偏好更高的吞吐量,这样可以同时服务更多的用户;但在另外一些场 景下,系统可能偏好低延迟或者响应时间短,这样可以快速返回单个请求,保证用户良好 的使用体验。

这两个指标相互影响,缺一不可。比如,系统可以支持很高的吞吐量,如果延迟达到 分钟级,高吞吐量就毫无意义;或者系统的延迟很低,吞吐量也很低,同样不行。一般情 况下,吞吐量越大,延迟或者响应时间就越大,因为请求量很大,导致系统繁忙,进而响 应时间会变大;而延迟越低,能支持的吞吐量就会越高,因为延迟低说明请求的处理速度 很快,系统可以处理更多的请求。

性能诊断是个复杂的过程,需要各方面的数据来综合判断。一般都需要经过确定应用 类型、查看操作系统数据、查看应用进程数据等过程才能最终定位问题。

确定应用类型 

在做性能诊断前,我们首先得清楚要诊断的应用程序有什么资源需求或者运行模式是 什么样的,一般情况下,我们把应用程序分为两类。

CPU 密集型:CPU 密集型的应用需要大量的 CPU 资源,这类应用需要 CPU 资源 做一些数学计算或者批处理。互联网应用中的 Web 服务器、邮件服务器等被认为 是 CPU 密集型应用。

I/O 密集型:I/O 密集型应用需要用到大量的内存及底层存储系统,主要是因为 它需要处理大量数据。I/O 密集型应用一般不需要 CPU 或者网络(云硬盘之类 的存储系统还是需要网络的),它使用 CPU 主要用于发起 I/O 请求,然后处理 等待状态。数据库应用一般被认为是 I/O 密集型应用。

这里的 CPU 密集型和 I/O 密集型应用并不是说只用到 CPU 或者 I/O,而是指影响应用 性能瓶颈的主要是 CPU 和 I/O。确定了应用的类型,也就知道了影响应用的主要因素,在 后面的排查过程中就能更有效地解读数据。

查看操作系统数据 

发现应用的情况出现问题时,不一定是应用本身有问题,也有可能只是操作系统资源 不够。所以我们要观察收集的操作系统数据,分析性能瓶颈是否在操作系统资源上。

首先是 CPU 利用率,如果 CPU 利用率不高,而系统的吞吐量不高或者延迟很高,就 说明我们的程序并没有忙于计算,而是忙于 I/O。其次我们可以看一下 I/O 的情况、等待时 间、磁盘使用率等。然后查看网络使用情况、流量是否占满带宽、连接状态是否有异常。
如果上述内容都没有问题,系统的性能还是低,就说明应用程序的代码有问题,比如, 程序被阻塞了,或者锁竞争比较多,或者在等待资源等。

如果瓶颈在系统资源上,通过分析操作系统的性能数据,就能发现具体的原因,比如 带宽不够,内存不够或者打开文件数限制等。这时最简单的做法就是调整硬件资源,或者 调整操作系统的配置。如果通过 3.3.3 节操作系统监控中提到的工具还无法获取到你想要的 数据,可以尝试通过图 4-30 中各个子系统详细的工具来查看相应的监控数据。 


图 4-30 Linux 性能监控工具 

查看应用进程数据 

如果操作系统本身的负载不高或者资源充足,我们就需要观察应用程序的性能指标。 观察应用进程状态,首先要做的就是先看看进程使用的系统资源,数据的获取可以参考之 前的应用进程监控。进程的 CPU 使用率、内存使用量、I/O 读写情况都有可能是问题线索。
CPU 

当进程的 CPU 指标异常时,如 CPU 使用率很高或很低,与之前的预期不一致,就需 要查看具体进程的 CPU 使用情况,或者进程内各个线程的运行情况,有可能因为某个线程 出问题(死锁、资源等待等),导致整体进程状态异常。top 及 pidstat 都可以给出某个进程 的 CPU 使用情况。使用合适的参数,还可以观察进程中线程的 CPU 使用情况。


pidstat 可以对指定进程或者进程内单个线程(-t 参数)的 CPU 使用率情况进行定期 统计。




磁盘

如果进程因为等待磁盘操作而导致性能下降,可以观察某个进程的 I/O 读写情况。 pidstat 可以对进程的 I/O 读写情况进行统计。


在一些场景下,你可能需要知道目标进程是否打开了某个文件(不仅限于磁盘文件, Linux 世界中,一切都是文件,如网络连接、硬件设置等)或者目标进程写日志的目录等, 可以通过 lsof 来达到目的。

网络 

当应用进程的网络行为不符合预期时,如无法接受新的连接,或者某个连接发送的数 据不符合预期等,就需要对具体的连接或者端口的数据进行观察才能确认问题。一般比较 常用的是 tcpdump 命令,它可以通过对 TCP 连接上的数据进行抓包,可以同时检验发出的 数据包及收到的数据包,方便确认因为网络包的原因而导致的问题。如果进程突然消失、 网络发生丢失或者其他系统事件,可以通过 dmesg 命令去查看。 

如上面的输出就提到了一个 Java 进程因为 OOM 而被系统杀死。

Java 应用性能诊断

jstack 

jstack 可以提供 Java 进程内部每个线程的运行情况,包括线程是否在运行、线程是否 在等待锁或者 I/O 等待。jstack 可以定位到线程堆栈,根据堆栈信息定位到具体的应用代码, 这些信息对于判断应用进程的运行状态及判断出问题的代码非常有用。以下代码就是一个 简单 jstack 输出。

jmap 

当发现程序的内存不足、GC 异常,或者怀疑有内存泄露问题时,可以通过 jmap 来获 得运行中 Java 程序堆内存的快照,以检查有哪些影响性能的大对象的创建,或者什么对象 的数据较多,以及各种对象占用内存的大小等。

常用的使用方式有两种,一种给出进程内所有对象的统计数据,比如哪个类型,创建 了多少个对象,占用了多少内存,并以文本格式输出结果,我们可以很直观地对两次的结 果进行对比。



另一种以二进制的形式输出结果,包含更多的信息,比如对象是谁创建的,谁在引用 等。生成结果后就可以通过 MAT 之类的工具来做分析。 


jstat 

如果没有通过 JVM 参数等方式来输出 GC 的情况,就可以通过 jstat 来实时观察 GC 情 况,比如发生的次数和耗时,同时 jstat 也可以实时观察堆内存的使用情况。


JVM 提供的工具一般只能显示 JVM 层面的信息,如果碰到线上问题,需要知道一些 代码层面的信息怎么办?我们可能在程序里打一些日志来排查,但每一次查看问题都需要 更改代码,重新部署,然后观察。这种方式对于在线应用的排查来说,一方面效率很低, 另一方面破坏问题出现的上下文,导致无法重现问题。而 Btrace 就是一个可以在不修改应 用代码、不重启进程的情况下,动态地将跟踪字节码注入运行类中,方便查看应用运行信息的工具。图 4-31 展示了 Btrace 的工作原理。


图 4-31 Btrace 的工作原理
具体如何使用 Btrace 可以参考它的用户手册。

文章节选自《云原生应用架构实践》 网易云基础服务架构团队 著 

参考文档:https://sq.163yun.com/blog/article/221367124985876480