文章目录
前言
在实际生产环境中总会出现平均负载高的告警、CPU使用率超过100%的告警等情况。因此如何分析出现该情况的原因,也是运维在实际环境维护中所作的一部分,本篇文章就带领大家一步步定位到问题
一、常用的Load分析方法
Load指的是平均负载1、5、15
1.1.CPU高、Load高
yaml
1、通过top命令查找占用CPU最高的进程PID
2、通过top -Hp PID 查找占用CPU最高的线程TID
3、对于java程序,查看进程的堆栈信息,使用jstack 进程PID打印进程的堆栈信息
4、通过printf %x tid 打印出最高消耗CPU线程的十六进制
1.2.CPU低、Load高
yaml
产生的原因:
等待磁盘I/O完成的进程太多,导致进程队列长度过大,但是cpu运行的进程却很少。
排查过程:
1、通过top命令查看CPU等待IO时间,即%wa
2、通过iostat -d -x -m 1 10查看磁盘IO情况(yum -y install sysstat)
3、通过sar -n DEV 1 10查看网络IO情况
4、通过以下命令查找占用IO的程序
ps -e -L h o state,cmd |awk '{if ($1=="R" || $1=="D"){print $0}}' |sort |uniq -c|sort -k 1nr
二、cpu高、Load高情况分析
使用vmstat 查看系统维度的CPU负载 使用top查看进程维度的CPU负载
1.使用vmstat查看系统维度的CPU负载
c
vmstat -n 1 表示结果一秒刷新一次
结果:
[root@l-jdy-prod-bjxfj-nginx3 ~]# vmstat -n 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 258308 0 7391508 0 0 2 7 0 0 1 0 99 0 0
0 0 0 258360 0 7391508 0 0 0 0 675 911 1 1 99 0 0
0 0 0 259000 0 7391508 0 0 0 12 662 964 2 1 98 0 0
0 0 0 259104 0 7391508 0 0 0 59 462 644 0 0 100 0 0
0 0 0 259080 0 7391532 0 0 0 0 516 760 0 1 100 0 0
r: 表示系统中CPU等待处理的线程。由于CPU每次只能处理一个线程,所以该数值越大表示系统运行越慢
b: 表示阻塞的进程
us: 用户CPU时间,当us接近100,r队列会到达80
sy: 系统cpu时间,如果太高表示系统调用时间长,例如是IO频繁操作
wa: IO等待消耗的CPU时间百分比。该值较高时说明IO等待比较严重,可能磁盘大量作随机访问造成的,也可能是磁 盘性能出现了瓶颈
id: 处于空闲状态的CPU时间百分比。如果该值持续为0,同时sy是us的两倍,则通常说明系统则面临着CPU资源的短 缺。
yaml
常见问题即解决方法:
1、如果r经常大于4,且id少于40,表示CPu负荷很重
2、如果pi、po长期不等于0,表示内存不足
3、如果disk常不等于0,且b中的队列大于3,表示IO性能不好
三、当CPU高时自动Dump堆栈信息
1.背景
c
当CPU使用率过高时,自动生成堆栈转储(Dump)对于诊断问题是非常有帮助的。
常用的方法是使用jstack来获取线程堆栈信息,分析哪些线程占用了过多的CPU。
但是对于某些偶发性的CPU过高,研发人员不可能实时盯着系统,
当发现CPU告警后,在去上服务器上找到主机在操作dump,可能此时CPU已经恢复,没有及时获取到"第一现场"。
其方式是通过脚本方式在固定时间能主动查看CPU使用率,如果过高则可以执行Dump堆栈操作。
2.CPU高时自动Dump堆栈信息脚本
shell
#!/bin/bash
# 可配置项:
# 触发dump的cpu阈值。default 70
# STACK_DUMP_CPU_THRESHOLD=xxx
# 触发dump时列举的线程数(按使用率由高到低排列) default 10
# STACK_DUMP_THREAD_COUNT=xxx
# 配置方式,使用行云分组的环境变量配置即可
# stack log 存放目录 /data/log/
# stack log 文件名: jstack_check_$(date +%Y%m%d%H%M%S).log
# 设置CPU阈值,当CPU使用率达到该阈值时触发线程快照
CPU_THRESHOLD="${STACK_DUMP_CPU_THRESHOLD:-80}"
THREAD_COUNT="${STACK_DUMP_THREAD_COUNT:-10}"
CPU_CORE=4
echo "Current CPU_THRESHOLD is $CPU_THRESHOLD"
JAVA_PID=$(pgrep -d, -x java)
echo "Current JAVA_PID is $JAVA_PID"
# 使用top命令获取当前CPU使用率,并提取其中的CPU利用率百分比
CPU_USAGE=$(top -b -n 1 | grep -A10 "PID USER" | grep java | grep "$JAVA_PID" | awk '{print $9}' | cut -d'.' -f1)
echo "Current Java($JAVA_PID) CPU_USAGE:$CPU_USAGE"
if [ -z "$JAVA_PID" ]; then
echo "No Java process found."
exit 1
fi
if [[ $CPU_USAGE -lt 0 ]]; then
exit 1
fi
PER_CPU_USAGE=$[CPU_USAGE / CPU_CORE]
# 检查CPU使用率是否超过阈值
if [[ $PER_CPU_USAGE -gt $CPU_THRESHOLD ]]; then
# 使用top命令查找占用CPU最高的前十个线程,并获取它们的信息
TOP_THREADS=$(top -H -b -n 1 -p "$JAVA_PID" | grep -A$THREAD_COUNT 'PID USER' | head -n $THREAD_COUNT | grep -v 'PID')
# 使用jstack捕捉JVM线程快照
# 请将下面的Java进程ID替换为你要监视的Java进程的实际进程ID
JSTACK_OUTPUT=$(/usr/local/jdk1.8.1_131/bin/jstack "$JAVA_PID")
JSTACK_OUTPUT_FILE="/export/Logs/jstack_snapshot_$(date +%Y%m%d%H%M%S).log"
echo "$JSTACK_OUTPUT" >>$JSTACK_OUTPUT_FILE
echo "====top 10 stack as below:====" >>$JSTACK_OUTPUT_FILE
echo "当前JAVA进程ID($JAVA_PID)CPU使用率:$PER_CPU_USAGE, $CPU_USAGE, $CPU_CORE"% >>$JSTACK_OUTPUT_FILE
# 获取占用CPU最高的前十个线程的信息,包括线程的PID和堆栈信息,并将它们合并到同一行输出
echo "Top ${THREAD_COUNT} CPU占用线程信息:" >>$JSTACK_OUTPUT_FILE
while read -r THREAD_INFO; do
THREAD_TID=$(echo "$THREAD_INFO" | awk '{print $1}')
THREAD_NID=$(printf "%x\n" $THREAD_TID)
THREAD_CPU_USAGE=$(echo "$THREAD_INFO" | awk '{print $9}')
echo "线程TID: $THREAD_TID, THREAD_NID:$THREAD_NID, CPU使用率: $THREAD_CPU_USAGE%" >>$JSTACK_OUTPUT_FILE
done <<<"$TOP_THREADS"
#echo "捕捉了JVM线程快照并保存到 $JSTACK_OUTPUT_FILE"
fi
注意事项
yaml
1、CPU使用率计算。因为我们服务往往都是多核,所以这个值会超过100。
而我们接到的CPU告警常常是单核高于某个阈值,基于此可以通过使用总使用率除以核数,就可以获取近似单核CPU使用率
2、top10高使用率输出仅打印线程ID(包括16进制和10进制的ID),打印每个线程的使用率值,方便快速方便高使用率线程。
输出示例:
JNI global references: 1820
====top 10 stack as below:====
当前JAVA进程ID(18406)CPU使用率:98, 393, 4%
Top 10 CPU占用线程信息:
线程TID: 19148, THREAD_NID:ac56, CPU使用率: 50.2%
线程TID: 384563, THREAD_NID:b2fd, CPU使用率: 23.5%
线程TID: 28173, THREAD_NID:2c56f, CPU使用率: 23.5%
线程TID: 4827, THREAD_NID:3619, CPU使用率: 23.5%
线程TID: 39587, THREAD_NID:48b0, CPU使用率: 17.6%
线程TID: 4257, THREAD_NID:c092, CPU使用率: 17.6%
线程TID: 195823, THREAD_NID:2c56c, CPU使用率: 17.6%
线程TID: 12468, THREAD_NID:2c56e, CPU使用率: 17.6%
线程TID: 8456, THREAD_NID:2c570, CPU使用率: 17.6%
注意事项:
1、脚本添加好之后,是需要重新部署后才能生效
2、检查脚本中的JDK的路径,和自己环境是否一致,检查参数:JSTACK_OUTPUT
3、当前服务的CPU核数为多少,检查参数:CPU_CORE
4、根据自身需求设置合理的触发dump最大CPU阈值,检查参数:CPU_THRESHOLD
5、检查生成文件的路径是否存在,最好配置在可以自动清除的路径下,检查参数:JSTACK_OUTPUT_FILE