前言:我们的场景并没有像BAT等大型互联网公司里的系统那么复杂,但是基本上也有一定的规模,暂且就认为我们是一个中型互联网系统。但是工具和思路都是差不多的,因为原理是一样的。同时下面提到的命令,具体详细的用法,我想google上已经有足够多的资源了,这里只说思路与方向。
首先我们分析一下服务器构造和操作系统,服务器我们可以简单的分解为CPU、内存、硬盘(其他部件的我们就不考虑了),操作系统:我们简单的认为操作系统是来协同上面说的CPU、内存、硬盘的一个系统,然而jdk bin目录里的应用程序最后都会运行在操作系统上,所以jdk会借助操作系统来操作CPU、内存、硬盘。 所以常见的线上问题,通常就出现在CPU、内存、硬盘和操作系统这个几个维度上。
这里总结一下我所碰到过的问题:CPU 利用率高、内存溢出、应用程序很卡(但是又没挂)、各种诡异问题(很难重现但总是偶尔出现)等。
首先我们来说内存,因为通常内存问题是最经常出的,常见的有内存溢出(OOM)、GC问题等。排查这些问题需要了解linux相关命令:
-
top 后,shift + m 来对各进程按内存进行排序
-
free 查看linux整体内存情况
OOM异常最好排查,因为已经溢出报错了,常用解决办法:
-
jmap 导出内存堆栈信息,并用第三方工具(比如MAT)分析,这些工具基本上可以展示到底是哪种对象最多。(这种是最常用的方法)
-
第三方工具在线展现具体的堆栈信息,比如:https://github.com/alibaba/TBJMap (我并没有实际用过,但是淘宝开源的一定是他们实际用过的,但是一般此类工具最好还是在测试环境中使用)
-
jprofiler、jvisualvm(具体怎么用,请google)
上面的几种工具和思路,已经基本能搞定OOM类型的问题了
GC问题:我们所碰到过的通常会因为GC回收过于频繁导致CPU过高,或者因为fullgc让整个java进程暂停。此类问题可以借助 jstat -gcutil pid查看各个区域和gc相关信息。当然一些在线工具(jprofiler、jvisualvm)也可以查看,但是不推荐线上使用,jstat已经很NB了。至于里面具体每个字段什么意思,google上已经非常多了。通常查jstat + jmap分析已经基本上能让你知道,是哪个地方的内存问题引起了GC问题。
但是,jstat上会出来很多信息,这里需要对java的内存管理有一定的了解,至少得知道,堆栈、分代机制、新生代、老生代等相关区域有一定的了解。总之要解决内存问题,得对java里的内存要了解。
CPU问题会相对来说复杂很多,CPU好比服务器的大脑,很多其他的组件也可能会导致CPU利用率过高。总结一下我们碰到过的有以下几中:
-
的确计算量过大,cpu运算不过来
-
IO读写过于频繁
-
java线程竞争锁过于频繁
-
应用程序很卡(但是又没挂)
要排查CPU相关的问题,首先得了解linux的top命令,top的功能非常多,具体每个属性什么意思请先google一下。
解决CPU类的问题总体思路:找到java进程中哪些线程使用CPU很高,简单的说就是top -H -p pid,这个命令会出现java进程里哪些线程使用CPU很高,会按降序排序,但是子线程编号与jstack pid 里出来的不太一样,jstack里出来的是16进程,所以把top -H -p里的转成16进程,然后查找一下具体的线程。
1.的确计算量过大,cpu运算不过来
通过上面的top -H -p和 jstack基本已经能看出来这种类型的问题
2.IO读写过于频繁
截图中的wa。这个代表是cpu等待io的时间,简单的说就是io正在处理,告诉cpu请等我一下,我处理完了就让你去干别的。因为cpu是固定资源,它被io所持有,那么别人就没有办法使用,所以就导致cpu使用率上去了。所以io wait很高就思路很清楚了,接下来就要去查看具体哪些线程在频繁操作io。
接下来就用jstack pid 来查看具体jdk中具体线程,先扫一眼,看是否有很多线程都处于io操作的阶段。但是这还不能完全解决问题,有的时候信息太多,实在不好找,一眼眼看,要看得头痛。所以简单点也可以使用第1点里提到的方法。
3.java线程竞争锁过于频繁
top -H -p和 jstack已经基本上能看出,但是这里还需要对java 线程的各种状态要了解一下。要能理解jstack出来的线程信息里每种状态是什么含义。这样才能分析出具体原因,不然跑去看代码总感觉不应该啊,应该没错啊。
4.应用程序很卡(但是又没挂)
这类问题如果业务不是很复杂,基本上用top -H -p 和 jstack可以查看得出来。但是有的时候还不能完全确定,一种常见的办法就是打日志,这种办法当然是OK的。但是这里再介绍一些可以直接在线上就能定位和分析的工具。https://github.com/alibaba/TProfiler,号称是可以线上使用的分析工具,淘宝开源的东西我们已经使用过很多了,基本上是靠谱的。
各种诡异问题:
所谓诡异问题,通常是飘忽不定,偶然出现,又难重现,搞得人非常想砸电脑的问题。但是它又的确出现了,我一直强调这个世界是科学的,碰到jvm编译器级别的BUG,我想有,但是可能不是你的这次案例,所以不要轻易怀疑是jvm的问题,我想不是像BAT等一些非常大型的系统很难碰到编译器级别的问题。
那么碰到这种问题不好重现,一种就是打日志,通过日志来排查程序运行的逻辑,比如一些多线程引起的逻辑问题。如果打日志太麻烦,而且又要重启很多东西,那么就可能要搬出神器来了,这里介绍两种(虽然两种都没有线上用来解决过实际问题,但按原理介绍应该是种解决问题的思路,毕竟不可能每种工具用过,了其原理就可以来判断是否能为己所用),第一种是BTrace、第二种是HouseMD(号称比BTrace更好用的工具)https://github.com/CSUG/HouseMD/wiki/UserGuideCN,阿里系也在使用,我想应该是靠谱的。他们的原理就是在线上运行的代码上动态的编织一些“料”进去,并能抽取其中一些结果来给调试者提供信息。
不过我的实践经验通常诡异问题是因为一些小细节的疏忽,或者一些低级错误。然后找到恨不得抽自己三个嘴巴。所以对原理的理解才能写出更好的代码。
其他碰到过的问题:
-
因为linux文件系统权限的问题,导致某些框架没有读权限,抛异常,而此类异常有的时候不是直接提示没有权限,比如有的可能会提示空指针异常,而这类问题去google通常是没有答案的。如果是空指针,那就好办了,去看源码。我想已经没有比空指针还好解决的问题了。
接下来我们总结一下,我们上面提到了下面这些关键词:
-
top
-
top -H -p (这个很有用)
-
jmap
-
tbjmap
-
jstack
-
jprofiler
-
jvisualvm
-
TProfiler
-
BTrace
-
HouseMD
-
java 线程状态
-
java 内存管理
每一种类型的都用过一次已经不错了,每一种类型的都在线上真实的排查过用过,那就更不错了,不过线上有这么多的问题也够严重的了。如果每一种工具和类型的问题都对其背后的工具和原理实践过、学习过,我想功力已经非常不错了。所以碰到每一个线上问题,都要有能够发散的思路,并能深入下去。而不是停留在工具这个层面。
工具上面已经介绍很多了,但是真正的场景中不可能每个去试,等试完所有的已经不叫快速了,通常做一个系统,会大致了解这个系统是IO密集型还是计算密集型应用,或者在开发的过程就已经知道了未来可能哪些地方会有增长。
通常出问题了,先上去看log4j和tomcat的日志,如果日志里能体现问题在哪里基本就到这里了,如果日志正常。那么通常是应用比较慢,那么先top。如果机器load高,找到CPU占用最高的进程。如果是java进程,可以先jstack 看个大概,如果还没有头绪, 就同时看一下GC情况,这时就要用jstat,gc正常,那就再用top -H -p,找CPU最高的地方。这么一套流程跑下来基本上能定位问题。如果load不高,那么情况就要复杂很多,要检查一些外围组件。
1、DB,看是不是很多SQL积压。如果sql也正常,别的表象都正常,那么就要利用类似TProfiler类似的工具(前提是你已经预先就部署好了)。
2、http,linux连接数是否很多,当前网络流量是不是跑满了,甚至我还碰到过某个http api所对应的域名dns有问题 ping 一下要几百ms。(淘宝还有网卡跑满的情况。。)
3、第三方本地应用慢,有的时候会使用一些linux上的工具,比如图片压缩。这些应也可能会影响程序。
上面的是我的常用思路和方法,但是具体的情况会有变化,比如我们现在在用的是阿里云的ECS,一台机器只有5M,所以带宽我会优先观察,如果带宽正常再看面的,所以具体的思路和方法还是要看对系统和当时的环境有多了解,需要做一些调整。
那让我们继续带着问题发散下去,接下来我们准备一些问题。
1、通常jstack 后 出来的线程堆栈信息后,再去top -H -p(或者反过来也可以)。中间总有时间差,可能jstack里的那个线程与top -H -p中出来的 那个已经对不上了。那这样可能并不能真实的反应实际情况。(当然通常是OK的)。那么有没有办法可以快速的来一个命令就能出来对应的线程?当然没有,那么没有就自己想原理能不能写嘛。这里应该有很多种办法可以做,比如可以通过shell把几个命令组合在一起,然后抽取对应的信息,然后分析,如果shell太复杂了,也可以用perl、python甚至java写一个linux上的小程序也是OK的啊。这样执行一个命令就可以快速的得到所有信息。再发散下去,有的时候不是每台机器都要登录上去,那能不能在某台特定的机器,甚至在自己的电脑上就可以去查看呢?是否可以做成网页,直接在浏览器上查看?
2、当出现问题后我们上去机器查看具体问题,我们认为自己是在拉信息,那么是否可以改成推模式?快有问题时推送数据到某个地方,说俗点就是监控和预警,那么我们使用的tomcat、jetty等容器是否可以在线监控?是否有预留接口?那要收集所有机器上的信息到同一个地方具体又有哪些解决方案。方案有一万种,全在google和基础原理中。
3、如果要做监控,那么怎么快速又灵活的添加监控项目,同时监控又不过于影响和侵入线上的业务,中间又有很多问题需要解决。
我想如果应用不是非常复杂,上面的这些方法和思路,基本上已经能解决绝大多数的问题了。
最后,我想线上问题最好的情况应该是被监控发现,就算要上去调试,用工具会比人手快,比如jstack 和 top -H -p用脚本去自动抽取top N最消耗CPU的子线程。