zoukankan      html  css  js  c++  java
  • 服务器线上问题排查研究

    线上问题诸如:

      1、线上服务器CPU占用率高如何排查?

      2、线上服务器Load飙高如何排查? 

      3、线上服务器频繁发生Full GC如何排查? 

      4、线上服务器发生死锁如何排查?


    一:线上服务器CPU占用率高如何排查?

    问题发现:

     在每次大促之前,我们的测试人员都会对网站进行压力测试,这个时候会查看服务的cpu、内存、load、rt、qps等指标。

     在一次压测过程中,测试人员发现我们的某一个接口,在qps上升到500以后,CPU使用率急剧升高。

    CPU利用率,又称CPU使用率。顾名思义,CPU利用率是来描述CPU的使用情况的,表明了一段时间内CPU被占用的情况。使用率越高,说明你的机器在这个时间上运行了很多程序,反之较少。

    问题定位:

     定位进程:登录服务器,执行top命令,查看CPU占用情况:

    top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器。

     通过以上命令,我们可以看到,进程ID为1893的Java进程的CPU占用率达到了181%,基本可以定位到是我们的Java应用导致整个服务器的CPU占用率飙升。

     定位线程

      我们知道,Java是单进程多线程的,那么,我们接下来看看PID=1893的这个Java进程中的各个线程的CPU使用情况,同样是用top命令: 

     

      通过top -Hp 1893命令,我们可以发现,当前1893这个进程中,ID为4519的线程占用CPU最高。

      定位代码 

      通过top命令,我们目前已经定位到导致CPU使用率较高的具体线程, 那么我么接下来就定位下到底是哪一行代码存在问题。

      首先,我们需要把4519这个线程转成16进制:

     

      接下来,通过jstack命令,查看栈信息:

     

      通过以上代码,我们可以清楚的看到,BeanValidator.java的第30行是有可能存在问题的。

     问题解决

      接下来就是通过查看代码来解决问题了,我们发现,我们自定义了一个BeanValidator,封装了Hibernate的Validator,然后在validate方法中,通过Validation.buildDefaultValidatorFactory().getValidator()初始化一个Validator实例,通过分析发现这个实例化的过程比较耗时。

      我们重构了一下代码,把Validator实例的初始化提到方法外,在类初始化的时候创建一次就解决了问题。

     总结 

      以上,展示了一次比较完成的线上问题定位过程。主要用到的命令有:top 、printf 和 jstack

      另外,线上问题排查还可以使用Alibaba开源的工具Arthas进行排查,以上问题,可以使用一下命令定位:

     

      以上,本文介绍了如何排查线上服务器CPU使用率过高的问题,如果大家感兴趣,后面可以再介绍一些关于LOAD飙高、频繁GC等问题的排查手段。


     二、线上服务器Load飙高如何排查? 

    什么是负载

    负载(load)是linux机器的一个重要指标,直观了反应了机器当前的状态。

    来看下负载的定义是怎样的:

    In UNIX computing, the system load is a measure of the amount of computational work that a computer system performs. The load average represents the average system load over a period of time. It conventionally appears in the form of three numbers which represent the system load during the last one-, five-, and fifteen-minute periods.(wikipedia)

      简单解释一下:在UNIX系统中,系统负载是对当前CPU工作量的度量,被定义为特定时间间隔内运行队列中的平均线程数。load average 表示机器一段时间内的平均load。这个值越低越好。负载过高会导致机器无法处理其他请求及操作,甚至导致死机。

      Linux的负载高,主要是由于CPU使用、内存使用、IO消耗三部分构成。任意一项使用过多,都将导致服务器负载的急剧攀升。

    查看机器负载。

      在Linux机器上,有多个命令都可以查看机器的负载信息。其中包括uptimetopw等。

    uptime命令

      uptime命令能够打印系统总共运行了多长时间和系统的平均负载。uptime命令可以显示的信息显示依次为:现在时间、系统已经运行了多长时间、目前有多少登陆用户、系统在过去的1分钟、5分钟和15分钟内的平均负载。

      这行信息的后半部分,显示”load average”,它的意思是”系统的平均负荷”,里面有三个数字,我们可以从中判断系统负荷是大还是小。

      1.74 1.87 1.97 这三个数字的意思分别是1分钟、5分钟、15分钟内系统的平均负荷。我们一般表示为load1、load5、load15。  

    何为系统负载呢?

     系统平均负载被定义为在特定时间间隔内运行队列中的平均进程数。如果一个进程满足以下条件则其就会位于运行队列中:

    • 它没有在等待I/O操作的结果
    • 它没有主动进入等待状态(也就是没有调用'wait')
    • 没有被停止(例如:等待终止)

      一般来说,每个CPU内核当前活动进程数不大于3,则系统运行表现良好!当然这里说的是每个cpu内核,也就是如果你的主机是四核cpu的话,那么只要uptime最后输出的一串字符数值小于12即表示系统负载不是很严重.当然如果达到20,那就表示当前系统负载非常严重,估计打开执行web脚本非常缓慢.  

    cat /proc/loadavg

     最直接查看系统平均负载命令

    root@Slyar.com:~# cat /proc/loadavg
    0.10 0.06 0.01 1/72 29632

     除了前3个数字表示平均进程数量外,后面的1个分数,分母表示系统进程总数,分子表示正在运行的进程数;最后一个数字表示最近运行的进程ID

    w命令

      w命令的主要功能其实是显示目前登入系统的用户信息。但是与who不同的是,w命令功能更加强大,w命令还可以显示:当前时间,系统启动到现在的时间,登录用户的数目,系统在最近1分钟、5分钟和15分钟的平均负载。然后是每个用户的各项数据,项目显示顺序如下:登录帐号、终端名称、远 程主机名、登录时间、空闲时间、JCPU、PCPU、当前正在运行进程的命令行。

     

      从上面的w命令的结果可以看到,当前系统时间是14:08,系统启动到现在经历了23小时41分钟,共有3个用户登录。系统在近1分钟、5分钟和15分钟的平均负载分别是1.74 1.87 1.97。这和uptime得到的结果相同。 下面还打印了一些登录的用户的各项数据,不详细介绍了。

    top命令

      top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器。

    机器正常负载范围

      对于机器的Load到底多少算正常的问题,一直都是很有争议的,不同人有着不同的理解。对于单个CPU,有人认为如果Load超过0.7就算是超出正常范围了。也有人认为只要不超过1都没问题。也有人认为,单个CPU的负载在2以下都可以接受。

      为什么会有这么多不同的理解呢,是因为不同的机器除了CPU影响之外还有其他因素的影响,运行的程序、机器内存、甚至是机房温度等都有可能有区别。

      比如,有些机器用于定时执行大量的跑批任务,这个时间段内,Load可能会飙的比较高。而其他时间可能会比较低。那么这段飙高时间我们要不要去排查问题呢?

      我的建议是,最好根据自己机器的实际情况,建立一个指标的基线(如近一个月的平均值),只要日常的load在基线上下范围内不太大都可以接收,如果差距太多可能就要人为介入检查了。

      但是,总要有个建议的阈值吧,关于这个值。阮一峰在自己的博客中有过以下建议:

    当系统负荷持续大于0.7,你必须开始调查了,问题出在哪里,防止情况恶化。

    当系统负荷持续大于1.0,你必须动手寻找解决办法,把这个值降下来。

    当系统负荷达到5.0,就表明你的系统有很严重的问题,长时间没有响应,或者接近死机了。你不应该让系统达到这个值。

      以上指标都是基于单CPU的,但是现在很多电脑都是多核的。所以,对一般的系统来说,是根据cpu数量去判断系统是否已经过载(Over Load)的。如果我们认为0.7算是单核机器负载的安全线的话,那么四核机器的负载最好保持在3(4*0.7 = 2.8)以下。

      还有一点需要提一下,在Load Avg的指标中,有三个值,1分钟系统负荷、5分钟系统负荷,15分钟系统负荷。我们在排查问题的时候也是可以参考这三个值的。

      一般情况下,1分钟系统负荷表示最近的暂时现象。15分钟系统负荷表示是持续现象,并非暂时问题。如果load15较高,而load1较低,可以认为情况有所好转。反之,情况可能在恶化。

    如何降低负载

     导致负载高的原因可能很复杂,有可能是硬件问题也可能是软件问题。

     如果是硬件问题,那么说明机器性能确实就不行了,那么解决起来很简单,直接换机器就可以了。

     前面我们提过,CPU使用、内存使用、IO消耗都可能导致负载高。如果是软件问题,有可能由于Java中的某些线程被长时间占用、大量内存持续占用等导致。建议从以下几个方面排查代码问题:

      1、是否有内存泄露导致频繁GC

      2、是否有死锁发生

      3、是否有大字段的读写

      4、会不会是数据库操作导致的,排查SQL语句问题。

     这里还有个建议,如果发现线上机器Load飙高,可以考虑先把堆栈内存dump下来后,进行重启,暂时解决问题,然后再考虑回滚和排查问题。

    Java Web应用Load飙高排查思路

    1、使用uptime查看当前load,发现load飙高。

    2、使用top命令,查看占用CPU较高的进程ID。、

      发现PID为1893的进程占用CPU 181%。而且是一个Java进程,基本断定是软件问题。

    3、使用 top命令 (top -Hp 进程id),查看具体是哪个线程占用率较高

    4、使用printf命令查看这个线程的16进制

    5、使用jstack命令查看当前线程正在执行的方法 (jstack 进程id | grep 线程id的16进制)。(Java命令学习系列(二)——Jstack)

      从上面的线程的栈日志中,可以发现,当前占用CPU较高的线程正在执行我代码的com.hollis.test.util.BeanValidator.validate(BeanValidator.java:30)类。那么就可以去排查这个类是否用法有问题了。

    网上有牛人写了一个脚本能自动帮我们大致定位到现场导致LOAD飙升的JVM线程,脚本大概如下

    复制代码
    #!/bin/ksh
    typeset top=${1:-10}
    typeset pid=${2:-$(pgrep -u $USER java)}
    typeset tmp_file=/tmp/java_${pid}_$$.trace
    
    $JAVA_HOME/bin/jstack $pid > $tmp_file
    ps H -eo user,pid,ppid,tid,time,%cpu --sort=%cpu --no-headers
            | tail -$top
            | awk -v "pid=$pid" '$2==pid{print $4"	"$6}'
            | while read line;
    do
            typeset nid=$(echo "$line"|awk '{printf("0x%x",$1)}')
            typeset cpu=$(echo "$line"|awk '{print $2}')
            awk -v "cpu=$cpu" '/nid='"$nid"'/,/^$/{print $0"	"(isF++?"":"cpu="cpu"%");}' $tmp_file
    done
    
    rm -f $tmp_file
    复制代码

    现在我们就来拆解其中的原理,以及说明下类似脚本的适用范围。
      1.使用top命令查看飙高的java进程,记录pid
      2.通过jstack命令将java的线程栈输出,保留现场 jstack -l 30142 > 30142.stack
      3.找到当前CPU使用占比高的线程,通过 ps H -eo user,pid,ppid,tid,time,%cpu –sort=%cpu
        USER:进程归属用户,PID:进程号,PPID:父进程号,TID:线程号
        %CPU:线程使用CPU占比(这里要提醒下各位,这个CPU占比是通过/proc计算得到,存在时间差)
      4.合并相关信息,通过PS拿到了TID,可以通过进制换算10-16得到jstack出来的JVM线程号​
        typeset nid=”0x”(echo"(echo"line”|awk ‘{print $1}’|xargs -I{} echo “obase=16;{}”|bc|tr ‘A-Z’ ‘a-z’)
      5.最后再将ps和jstack出来的信息进行一个匹配与合并。终于,得到我们最想要的信息



    三:线上服务器频繁发生Full GC如何排查

    1:查看JVM堆配置参数

    2:linux下查看java虚拟机(JVM)GC情况

    3:导出堆栈的dump信息分析

    jstat命令命令格式:

    jstat [Options] vmid [interval] [count]
     
    参数说明:
      Options,选项,我们一般使用 -gcutil 查看gc情况
      vmid,VM的进程号,即当前运行的java进程号
      interval,间隔时间,单位为秒或者毫秒
      count,打印次数,如果缺省则打印无数次
     
    实例说明

    示例

    通常运行命令如下:
      jstat -gc 12538 5000
    即会每5秒一次显示进程号为12538的java进成的GC情况,
    显示内容如下图:
     
    结果说明
     显示内容说明如下(部分结果是通过其他其他参数显示的,暂不说明):
             S0C:年轻代中第一个survivor(幸存区)的容量 (字节) 
             S1C:年轻代中第二个survivor(幸存区)的容量 (字节) 
             S0U:年轻代中第一个survivor(幸存区)目前已使用空间 (字节) 
             S1U:年轻代中第二个survivor(幸存区)目前已使用空间 (字节) 
             EC:年轻代中Eden(伊甸园)的容量 (字节) 
             EU:年轻代中Eden(伊甸园)目前已使用空间 (字节) 
             OC:Old代的容量 (字节) 
             OU:Old代目前已使用空间 (字节) 
             PC:Perm(持久代)的容量 (字节) 
             PU:Perm(持久代)目前已使用空间 (字节) 
             YGC:从应用程序启动到采样时年轻代中gc次数 
             YGCT:从应用程序启动到采样时年轻代中gc所用时间(s) 
             FGC:从应用程序启动到采样时old代(全gc)gc次数 
             FGCT:从应用程序启动到采样时old代(全gc)gc所用时间(s) 
             GCT:从应用程序启动到采样时gc用的总时间(s) 
             NGCMN:年轻代(young)中初始化(最小)的大小 (字节) 
             NGCMX:年轻代(young)的最大容量 (字节) 
             NGC:年轻代(young)中当前的容量 (字节) 
             OGCMN:old代中初始化(最小)的大小 (字节) 
             OGCMX:old代的最大容量 (字节) 
             OGC:old代当前新生成的容量 (字节) 
             PGCMN:perm代中初始化(最小)的大小 (字节) 
             PGCMX:perm代的最大容量 (字节)   
             PGC:perm代当前新生成的容量 (字节) 
             S0:年轻代中第一个survivor(幸存区)已使用的占当前容量百分比 
             S1:年轻代中第二个survivor(幸存区)已使用的占当前容量百分比 
             E:年轻代中Eden(伊甸园)已使用的占当前容量百分比 
             O:old代已使用的占当前容量百分比 
             P:perm代已使用的占当前容量百分比 
             S0CMX:年轻代中第一个survivor(幸存区)的最大容量 (字节) 
             S1CMX :年轻代中第二个survivor(幸存区)的最大容量 (字节) 
             ECMX:年轻代中Eden(伊甸园)的最大容量 (字节) 
             DSS:当前需要survivor(幸存区)的容量 (字节)(Eden区已满) 
             TT: 持有次数限制 
             MTT : 最大持有次数限制

     若发现GC频繁则可能是内存泄露等情况

  • 相关阅读:
    .Net数据集导出到Excel样式细节---------------摘自别人的
    导出word excel 方法
    constructor
    计算程序运行时间(.net1.1 于.net2.0的区别)在.net2.0中提供了Stopwatch类,简单例子
    委托 事件 observer
    easyui及读取xml
    MVC4.0 上传文件
    Asp.net MVC验证那些事(1)-- 介绍和验证规则使用----[转]--[并修改了部分内容]
    Educational Codeforces Round 54 (Rated for Div. 2) C. Meme Problem
    素数表的获取,埃氏筛法 介绍(复杂度 : O(n*loglogn) )
  • 原文地址:https://www.cnblogs.com/mrchenzheng/p/12107445.html
Copyright © 2011-2022 走看看