TSF微服务中java应用出现性能问题排查思路

2020-11-03 11:50:27 浏览数 (2)

问题背景

应用系统出现运行缓慢

目前采用微服务架构已经逐渐成为企业架构的标准范式,而大多微服务是基于Spring Cloud框架来进行应用的构建的,所以在开发实践中,甚至生产环境中,会遇到java相关问题,例如系统运行变慢、内存OOM,堆栈异常等问题,这里结合我之前的一些实践提供一些相关工具,和大家一起分享我们的诊断思路和解决技巧。

当出现系统变慢的时候,我们需要定位清楚这个变慢的表现:

1、服务是突然变慢还是长时间运行后观察到变慢?类似问题是否重复出现?

2、“慢”的定义是什么,是系统对其他方面的请求的反应延时变长吗?

理清问题的症状,这更便于我们接下来定位具体的原因。

定位分析思路

1、问题可能来自于 Java 服务自身,也可能仅仅是受系统里其他服务的影响。初始判断可以先确认是否出现了意外的程序错误,例如检查应用本身的错误日志。对于分布式系统,很多公司都会实现更加系统的日志、性能等监控系统。一些 Java 诊断工具也可以用于这个诊断,例如通过 JFR(Java Flight Recorder),监控应用是否大量出现了某种类型的异常。如果有,那么异常可能就是个突破点。如果没有,可以先检查系统级别的资源等情况,监控 CPU、内存等资源是否被其他进程大量占用,并且这种占用是否不符合系统正常运行状况。

2、监控 Java 服务自身,例如 GC 日志里面是否观察到 Full GC 等恶劣情况出现,或者是否 Minor GC 在变长等;利用 jstat 等工具,获取内存使用的统计信息也是个常用手段;利用 jstack 等工具检查是否出现死锁等。

3、如果还不能确定具体问题,对应用进行 Profiling 也是个办法,但因为它会对系统产生侵入性,如果不是非常必要,大多数情况下并不建议在生产系统进行。

4、定位了程序错误或者 JVM 配置的问题后,就可以采取相应补救措施,然后验证是否解决,否则还需要重复上面部分过程

整个具体定位排障过程分为:1、服务端系统性能分析。2、JVM层面的性能分析。

java服务端运行系统性能分析

系统性能分析中,CPU、内存和 IO 是主要关注项。

对于 CPU,如果是常见的 Linux,可以先用 top 命令查看负载状况。如下图所示:

top命令top命令

可以看到,其平均负载(load average)的三个值(分别是 1 分钟、5 分钟、15 分钟)非常低,并且暂时看并没有升高迹象。如果这些数值非常高(例如,超过 50%、60%),并且短期平均值高于长期平均值,则表明负载很重;如果还有升高的趋势,那么就要非常警惕了。

如果我们定位到CPU持续增高,怎么找到最耗费 CPU 的 Java 线程,下面我们简要介绍步骤:

1、输入命令:top –H,利用 top 命令获取相应 pid,“-H”代表 thread 模式,你可以配合 grep 命令更精准定位。

2、top –H然后转换成为 16 进制。输入命令为:printf "%x" your_pid

3、最后利用 jstack 获取的线程栈,对比相应的 ID 即可。

当然,还有更加通用的诊断方向,利用 vmstat 之类,查看上下文切换的数量,比如下面就是指定时间间隔为 1,收集 10 次。

输入命令如下:vmstat -1 -10,结果如下图所示:

vmstat 命令结果vmstat 命令结果

如果每秒上下文(cs,context switch)切换很高,并且比系统中断高很多(in,system interrupt),就表明很有可能是因为不合理的多线程调度所导致。当然还需要利用pidstat等手段,进行更加具体的定位。。

除了 CPU,内存和 IO 是重要的注意事项,比如:利用 free 之类查看内存使用。或者,进一步判断 swap 使用情况,top 命令输出中 Virt 作为虚拟内存使用量,就是物理内存(Res)和 swap 求和,所以可以反推 swap 使用。显然,JVM 是不希望发生大量的 swap 使用的。

对于 IO 问题,既可能发生在磁盘 IO,也可能是网络 IO。例如,利用 iostat 等命令有助于判断磁盘的健康状况。也会遇到过一些服务器本身IO性能问题,拖累了整体性能,解决办法就是申请替换了机器。

JVM 层面的性能分析

Java 是基于 JVM 上运行的,大部分内存都是在 JVM 的用户内存中创建的,所以除了通过以上 Linux 命令来监控整个服务器内存的使用情况之外,我们更需要知道 JVM 中的内存使用情况。JDK 中就自带了很多命令工具可以监测到 JVM 的内存分配以及使用情况。

对于 JVM 层面的性能分析,我们可以利用 JMC、JConsole 等工具进行运行时监控。利用各种工具,在运行时进行堆转储分析,或者获取各种角度的统计数据(如jstat -gcutil 分析 GC、内存分带等)。GC 日志等手段,诊断 Full GC、Minor GC,或者引用堆积等。

在这里我将分别展示使用 JDK 自带的工具来排查 JVM 参数配置问题、使用 Wireshark 来分析网络问题、通过 MAT 来分析内存问题,以及使用 Arthas 来分析 CPU 使用高的问题。

使用 JDK 自带工具查看 JVM 情况

JDK 自带了很多命令行甚至是图形界面工具,帮助我们查看 JVM 的一些信息。比如,在Linux机器上运行 ls 命令,可以看到 JDK 8 提供了非常多的工具或程序:

JDK自带的工具JDK自带的工具

JDK自带工具的基本作用如下:

JDK自带命令行工具作用JDK自带命令行工具作用

下面就分别介绍几个典型的JVM自带的工具:

JDK 工具之 jstat 命令

jstat 可以监测 Java 应用程序的实时运行情况,包括堆内存信息以及垃圾回收信息。我们可以运行 jstat -help 查看一些关键参数信息:

jstatjstat

再通过 jstat -option 查看 jstat 有哪些操作:

jstat操作jstat操作

JDK 工具之 jstack 命令

jstack是一种线程堆栈分析工具,最常用的功能就是使用 jstack pid 命令查看线程的堆栈信息,通常会结合 top -Hp pid 或 pidstat -p pid -t 一起查看具体线程的状态,也经常用来排查一些死锁的异常。

每个线程堆栈的信息中,都可以查看到线程 ID、线程的状态(wait、sleep、running 等状态)以及是否持有锁等。

jstack命令jstack命令

JDK 工具之 jmap 命令

jmap 查看堆内存初始化配置信息以及堆内存的使用情况。那么除了这个功能,我们其实还可以使用 jmap 输出堆内存中的对象信息,包括产生了哪些对象,对象数量多少等。我们可以用 jmap 来查看堆内存初始化配置信息以及堆内存的使用情况:

jmap命令jmap命令

我们可以使用 jmap -histo[:live] pid 查看堆内存中的对象数目、大小统计直方图,如果带上 live 则只统计活对象:

jmap命令jmap命令

我们可以通过 jmap 命令把堆内存的使用情况 dump 到文件中:

jmap dump文件jmap dump文件

我们可以将文件下载下来,使用 MAT 工具打开文件进行分析:

MAT工具分析MAT工具分析

我们平时遇到的内存溢出问题一般分为两种,一种是由于大峰值下没有限流,瞬间创建大量对象而导致的内存溢出;另一种则是由于内存泄漏而导致的内存溢出。使用限流,我们一般就可以解决第一种内存溢出问题,但其实很多时候,内存溢出往往是内存泄漏导致的,这种问题就是程序的 BUG,我们需要及时找到问题代码。

0 人点赞