共计 20017 个字符,预计需要花费 51 分钟才能阅读完成。
前言
==========
为什么需要做服务器 jvm 自动发现的监控呢?这个事情主要有两点原因:
1.zabbix 默认监控 jvm 状态是使用 jmx 中转进行监控的,监控效率比较低下
2.zabbix 使用 jmx 监控 jvm 的时候由于一个主机上的键值不能重复,也就导致了一台主机上只能监控一个 jvm 实例
以上两点原因导致 zabbix 通过 jmx 监控 jvm 的实现不是很理想,加上最近老大要求收集服务器上面跑的所有 Java 应用的信息,于是自己琢磨了下,还是自己动手,丰衣足食。利用了周末的时间,通过使用 shell 脚本 +java 工具 jstat+zabbix 实现监控主机上多 jvm 实例的功能。
第一章:概念的理解
首先,既然要监控 jvm 状态,那就必须要了解 jvm 里面的信息,楼主通过搜索资料加自动脑补,把网上的资料取其精华,去其糟粕,整理了一下。JVM 中的内存分类分为堆内存和非堆内存,堆内存是给实际应用使用的,非堆内存是给 jvm 容器使用的。我们主要关心的是堆内存这块。在堆内存里面,给内存分为如下几块:
1.Young 代(年轻代)
2.Old 代(老年代)
3.Perm 代(永久代)(关于这一点,在 JDK7 和 JDK8 中情况不一样,将在后面进行分析)
其中,年轻代里面又分成了三块,如下:
1.Eden 代(伊甸园代)
2.survivor0 代(0 号幸存区)
3.survivor1 代(1 号幸存区)
至于更详细的关于 JVM 堆内存的信息,各位可以自行百度或者 google,我这里就不赘述了,毕竟我也是个半桶水,自己找了点资料外加脑补到的一些东西,不敢在关公门前耍大刀了。
当然,还得科普一个东西,那就是 GC,所谓的 GC 就是 JVM 在运行的时候会有一个垃圾回收机制,这个垃圾回收机制是什么情况呢?就是在程序运行的时候会产生很多已经不使用的空间,但还是被占用了的情况,这样会造成很多不必要的浪费,于是 JVM 就有一个垃圾回收机制,针对程序中已经不使用的内存资源,会进行回收释放,这个过程就叫做 GC。当然,关于 GC 还有很多内容我这里也没有详述,理由同上条。各位看官只需要知道 GC 是 JVM 监控里面的一个很重要的参数就行了。
第一章,关于 JVM 中概念的理解结束了,预知后事如何,请听下回分解。
第二章:JAVA 工具的选用
java 工具有很多,关于 jvm 监控的工具主要有如下几个:
+ jstat
+ jmap
+ jstack
其中 jmap –heap pid 可以抓出挺多的关于某个 jvm 的运行参数,但是老大提醒我最好不要使用 jmap 进行 jvm 监控,具体没有说明原因。于是本着打破砂锅问到底的精神,我又去搜了一把,发现了如下内容:
jmap 最主要的危险操作是下面这三种:
1. jmap -dump
这个命令执行,JVM 会将整个 heap 的信息 dump 写入到一个文件,heap 如果比较大的话,就会导致这个过程比较耗时,并且执行的过程中为了保证 dump 的信息是可靠的,所以会暂停应用。
2. jmap -permstat
这个命令执行,JVM 会去统计 perm 区的状况,这整个过程也会比较的耗时,并且同样也会暂停应用。
3. jmap -histo:live
这个命令执行,JVM 会先触发 gc,然后再统计信息。
上面的这三个操作都将对应用的执行产生影响,所以建议如果不是很有必要的话,不要去执行。
所以,从上面三点来看,jmap 命令对 jvm 状态影响还是比较大的,而且执行 jmap –heap 的时间也比较长,效率较低,予以排除。
接下来是 jstack,这个命令可以深入到 JVM 里面对 JVM 运行问题进行排查,据说还可以统计 JVM 里面的线程数量。但是这个命令执行效率也比较低,被排除掉了。
于是剩下的只有一个 jstat 命令了。下面来详细的讲解该命令的使用了,咳咳,各位快点打起点精神来,这可是重头戏来了。
首先,列出 jstat 命令的一些使用案例吧:
============================================
1.jstat -gc pid
可以显示 gc 的信息,查看 gc 的次数,及时间。
其中最后五项,分别是 young gc 的次数,young gc 的时间,full gc 的次数,full gc 的时间,gc 的总时间。
S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT
9792.0 10048.0 0.0 5143.2 242048.0 220095.4 323200.0 211509.3 186368.0 114451.6 317 4.850 4 0.971 5.821
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
1024.0 1024.0 0.0 320.0 11776.0 11604.6 260608.0 149759.6 39344.0 38142.6 4528.0 4303.1 5473 24.010 2 0.128 24.138
2.jstat -gccapacity pid
可以显示,VM 内存中三代(young,old,perm)对象的使用和占用大小,
如 PGCMN 显示的是最小 perm 的内存使用量,PGCMX 显示的是 perm 的内存最大使用量,
PGC 是当前新生成的 perm 内存占用量,PC 是但前 perm 内存占用量。
其他的可以根据这个类推,OC 是 old 内纯的占用量。
NGCMN NGCMX NGC S0C S1C EC OGCMN OGCMX OGC OC PGCMN PGCMX PGC PC YGC FGC
87360.0 262144.0 262144.0 9792.0 10048.0 242048.0 174784.0 786432.0 323200.0 323200.0 131072.0 262144.0 186368.0 186368.0 317 4
NGCMN NGCMX NGC S0C S1C EC OGCMN OGCMX OGC OC MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC
1536.0 174592.0 13312.0 512.0 512.0 11776.0 260608.0 349696.0 260608.0 260608.0 0.0 1083392.0 39344.0 0.0 1048576.0 4528.0 5474 2
3.jstat -gcutil pid
统计 gc 信息统计。
S0 S1 E O P YGC YGCT FGC FGCT GCT
0.00 51.19 83.29 65.44 61.41 317 4.850 4 0.971 5.821
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
68.75 0.00 46.74 57.47 96.95 95.03 5474 24.014 2 0.128 24.143
4.jstat -gcnew pid
年轻代对象的信息。
S0C S1C S0U S1U TT MTT DSS EC EU YGC YGCT
9792.0 10048.0 0.0 5143.2 3 15 9792.0 242048.0 198653.2 317 4.850
S0C S1C S0U S1U TT MTT DSS EC EU YGC YGCT
512.0 512.0 352.0 0.0 15 15 512.0 11776.0 8446.4 5474 24.014
5.jstat -gcnewcapacity pid
年轻代对象的信息及其占用量。
NGCMN NGCMX NGC S0CMX S0C S1CMX S1C ECMX EC YGC FGC
87360.0 262144.0 262144.0 87360.0 9792.0 87360.0 10048.0 262016.0 242048.0 317 4
NGCMN NGCMX NGC S0CMX S0C S1CMX S1C ECMX EC YGC FGC
1536.0 174592.0 13312.0 57856.0 512.0 57856.0 512.0 173568.0 11776.0 5475 2
6.jstat -gcold pid
old 代对象的信息。
PC PU OC OU YGC FGC FGCT GCT
186368.0 114451.6 323200.0 211509.3 317 4 0.971 5.821
MC MU CCSC CCSU OC OU YGC FGC FGCT GCT
39344.0 38142.6 4528.0 4303.1 260608.0 149783.6 5475 2 0.128 24.148
7.jstat -gcoldcapacity pid
old 代对象的信息及其占用量。
OGCMN OGCMX OGC OC YGC FGC FGCT GCT
174784.0 786432.0 323200.0 323200.0 317 4 0.971 5.821
OGCMN OGCMX OGC OC YGC FGC FGCT GCT
260608.0 349696.0 260608.0 260608.0 5475 2 0.128 24.148
8.jstat -gcpermcapacity pid
perm 对象的信息及其占用量。
PGCMN PGCMX PGC PC YGC FGC FGCT GCT
131072.0 262144.0 186368.0 186368.0 317 4 0.971 5.821
没有
9.jstat -class pid
显示加载 class 的数量,及所占空间等信息。
Loaded Bytes Unloaded Bytes Time
25315 45671.7 5976 7754.1 15.19
Loaded Bytes Unloaded Bytes Time
6472 11893.0 0 0.0 5.97
10.jstat -compiler pid
显示 VM 实时编译的数量等信息。
Compiled Failed Invalid Time FailedType FailedMethod
4219 3 0 63.36 1 org/aspectj/weaver/ResolvedType addAndRecurse
Compiled Failed Invalid Time FailedType FailedMethod
11364 1 0 107.53 1 sun/nio/cs/UTF_8$Decoder decode
11.stat -printcompilation pid
当前 VM 执行的信息。
Compiled Size Type Method
4219 2232 1 net/spy/memcached/protocol/ascii/BaseGetOpImpl initialize
Compiled Size Type Method
11364 212 1 com/alibaba/rocketmq/client/impl/consumer/RebalanceService run
==================================================
可以看出上面我列出的命令执行结果为什么有两行呢,这是因为是用不同的 jdk 版本执行的。
上面是 JDK7 执行结果,下面是 JDK8 执行结果,这两个版本之间输出的结果是有差距的,下面,就来分析为什么会产生这种差异。
JDK7 和 JDK8 中 JVM 堆内存划分差异
如果记性好的童鞋们应该还能记得我上面在介绍 JVM 堆内存分类的时候括号里写的那个东东吧,没错,就是这个东西导致的。在 JDK7 中的 Perm 代(永久代)在 JDK8 中被废除了,取而代之的是 Metadata 代(元数据代),据说这个元数据代相对于永久代进行了优化,如果不设置最大值的话,默认会按需增长,不会造成像 Perm 代中内存占满后会爆出内存溢出的错误,元数据代也可以设置最大值,这样的话,当内存区域被消耗完的时候将会和 Perm 代一样爆出内存溢出的错误。(PS: 原谅我的班门弄斧,只能解释到这一个层面了。)
好了,解释清楚了 JDK7 和 JDK8 的差异以后,接下来我们来解释 jstat 抓到的这些参数了。
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960 jstat 命令获取参数解析
======================================================================================
* S0C 年轻代中第一个 survivor(幸存区)的容量 (字节)jstat -gcnew $pid|tail -1|awk ‘{print $1*1024}’
* S0U 年轻代中第一个 survivor(幸存区)目前已使用空间 (字节)jstat -gcnew $pid|tail -1|awk ‘{print $3*1024}’
* S0 年轻代中第一个 survivor(幸存区)已使用的占当前容量百分比 jstat -gcutil $pid|tail -1|awk ‘{print $1}’
* S0CMX 年轻代中第一个 survivor(幸存区)的最大容量 (字节)jstat -gcnewcapacity $pid|tail -1|awk ‘{print $4*1024}’
*
* S1C 年轻代中第二个 survivor(幸存区)的容量 (字节)jstat -gcnew $pid|tail -1|awk ‘{print $2*1024}’
* S1U 年轻代中第二个 survivor(幸存区)目前已使用空间 (字节)jstat -gcnew $pid|tail -1|awk ‘{print $4*1024}’
* S1 年轻代中第二个 survivor(幸存区)已使用的占当前容量百分比 jstat -gcutil $pid|tail -1|awk ‘{print $2}’
* S1CMX 年轻代中第二个 survivor(幸存区)的最大容量 (字节)jstat -gcnewcapacity $pid|tail -1|awk ‘{print $6*1024}’
* DSS 当前需要 survivor(幸存区)的容量 (字节)(Eden 区已满)jstat -gcnew $pid|tail -1|awk ‘{print $7*1024}’
*
* EC 年轻代中 Eden(伊甸园)的容量 (字节)jstat -gcnew $pid|tail -1|awk ‘{print $8*1024}’
* EU 年轻代中 Eden(伊甸园)目前已使用空间 (字节)jstat -gcnew $pid|tail -1|awk ‘{print $9*1024}’
* ECMX 年轻代中 Eden(伊甸园)的最大容量 (字节)jstat -gcnewcapacity $pid|tail -1|awk ‘{print $8*1024}’
* E 年轻代中 Eden(伊甸园)已使用的占当前容量百分比 jstat -gcutil $pid|tail -1|awk ‘{print $3}’
*
* NGCMN 年轻代 (young) 中初始化 (最小) 的大小 (字节)jstat -gccapacity $pid|tail -1|awk ‘{print $1*1024}’
* NGCMX 年轻代 (young) 的最大容量 (字节)jstat -gccapacity $pid|tail -1|awk ‘{print $2*1024}’
* NGC 年轻代 (young) 中当前的容量 (字节)jstat -gccapacity $pid|tail -1|awk ‘{print $3*1024}’
*
* OC Old 代的容量 (字节)jstat -gcold $pid|tail -1|awk ‘{print $3*1024}’
* OU Old 代目前已使用空间 (字节)jstat -gcold $pid|tail -1|awk ‘{print $4*1024}’
* OGCMX old 代的最大容量 (字节)jstat -gccapacity $pid|tail -1|awk ‘{print $8*1024}’
* OGCMN old 代中初始化 (最小) 的大小 (字节)jstat -gccapacity $pid|tail -1|awk ‘{print $7*1024}’
* O old 代已使用的占当前容量百分比 jstat -gcutil $pid|tail -1|awk ‘{print $4}’
* OGC old 代当前新生成的容量 (字节)jstat -gccapacity $pid|tail -1|awk ‘{print $9*1024}’
*
* PC Perm(持久代)的容量 (字节)jstat -gccapacity $pid|tail -1|awk ‘{print $14*1024}’
* PU Perm(持久代)目前已使用空间 (字节)jstat -gc $pid|tail -1|awk ‘{print $10*1024}’
* PGCMX perm 代的最大容量 (字节)jstat -gccapacity $pid|tail -1|awk ‘{print $12*1024}’
* PGCMN perm 代中初始化 (最小) 的大小 (字节)jstat -gccapacity $pid|tail -1|awk ‘{print $11*1024}’
* P perm 代已使用的占当前容量百分比 jstat -gcutil $pid|tail -1|awk ‘{print $5*1024}’
* PGC perm 代当前新生成的容量 (字节)jstat -gccapacity $pid|tail -1|awk ‘{print $13*1024}’
*
* YGC 从应用程序启动到采样时年轻代中 gc 次数 jstat -gccapacity $pid|tail -1|awk ‘{print $15}’
* YGCT 从应用程序启动到采样时年轻代中 gc 所用时间(s)jstat -gcutil $pid|tail -1|awk ‘{print $7}’
* FGC 从应用程序启动到采样时 old 代(全 gc)gc 次数 jstat -gccapacity $pid|tail -1|awk ‘{print $16}’
* FGCT 从应用程序启动到采样时 old 代(全 gc)gc 所用时间(s)jstat -gcutil $pid|tail -1|awk ‘{print $9}’
* GCT 从应用程序启动到采样时 gc 用的总时间(s)jstat -gcutil $pid|tail -1|awk ‘{print $10}’
*
* TT 持有次数限制 jstat -gcnew $pid|tail -1|awk ‘{print $5}’
* MTT 最大持有次数限制 jstat -gcnew $pid|tail -1|awk ‘{print $6}’
*
* Loadedjvm 加载 class 数量
* Unloadedjvm 未加载 class 数量
*
* M 元数据区使用比例
* MC 当前元数据空间大小
* MU 元数据空间使用大小
* MCMN 最小元数据容量
* MCMX 最大元数据容量
*
* CCS 压缩使用比例
* CCSC 当前压缩类空间大小
* CCSU 压缩类空间使用大小
* CCSMN 最小压缩类空间大小
* CCSMX 最大压缩类空间大小
====================================================
好了,上面就是我找到的一些对 jstat 获取的数据意思的统计,各位看官可以做个参考。
好了,这一章的内容到此基本结束,前面的东西都是一些理论类的东西,没有实际的操作。俗话说,光说不练假把式。接下来,我们将开启下一章的旅程,脚本 +jstat 的使用。
第三章:脚本 +jstat 获取数据
首先,我们来看一下该章节介绍的几个脚本吧:
1.jvm_list.sh 获取该机器上所有运行的 JVM 的进程对应的程序根目录以及程序名称
2.get_jvmlist.sh 将获取的该机器上的所有进程对应的程序名称序列化成 json 格式并发送给 zabbix 服务器
3.get_jvmstatus.sh 通过获取的程序根目录获取到对应的程序进程,再通过 jstat 抓取数据写入到文件中缓存
4.set_jvmstatus.sh zabbix 通过调用该脚本获取缓存文件中的关于某个 JVM 进程的状态信息
好了,简单介绍了上面几个脚本的功能,下面我们列出这几个脚本的实际内容:
#cat jvm_list.sh
#!/bin/bash
packagePath=/usr/local/etc/scripts/package_path.txt
echo -n >$packagePath
for i in `ps -fC java|tail -n +2|grep -v ‘flume’|awk ‘{print $2}’`;
do
pgrootpath=`ls -l /proc/$i/cwd|awk ‘{print $NF}’`
if [[-r $pgrootpath/appconfig]] && [`grep ^packagename= $pgrootpath/appconfig|wc -l`==1];then
packagename=$(grep ^packagename= $pgrootpath/appconfig 2>/dev/null|awk -F'”‘ ‘{print $2}’)
elif [[-r $pgrootpath/webconfig]] && [`grep ^packagename= $pgrootpath/webconfig|wc -l`==1];then
packagename=$(grep ^packagename= $pgrootpath/webconfig 2>/dev/null|awk -F'”‘ ‘{print $2}’)
else
packagename=$(basename $pgrootpath)-1.0.0-bin.tar.gz
fi
echo “$packagename $pgrootpath” >> $packagePath
done
该脚本的目的是先通过使用 ps -fC java 命令获取该机器上面除了 flume 进程外的所有其他 java 进程(我这边使用的是 flume 来收集业务日志的。)
然后,通过获取到的 PID 使用 ll /proc/pid/cwd 命令获取该进程的程序根目录,后面那些判断是获取该进程对应的包名(这一步各位可以根据自己公司的情况自行修改,我这边取包名的方式并不能够匹配各位公司的设置,在下爱莫能助了。)
最后是将获取到的程序根目录和包名存放在变量 packagePath 对应的文件中。
#cat get_jvmlist.sh
#!/bin/bash
TABLESPACE=`awk ‘{print $1}’ /usr/local/etc/scripts/package_path.txt`
COUNT=`echo “$TABLESPACE” |wc -l`
INDEX=0
echo ‘{“data”:[‘
echo “$TABLESPACE” | while read LINE; do
echo -n ‘{“{#TABLENAME}”:”‘$LINE'”}’
INDEX=`expr $INDEX + 1`
if [$INDEX -lt $COUNT]; then
echo ‘,’
fi
done
echo ‘]}’
这个脚本的作用就是通过读取文件里面的包名,然后将包名进行 json 序列化输出,没什么好讲的,套路套一个循环脚本就行。
接下来就是重要的脚本了,调用 jstat 获取 JVM 状态,并缓存到文件中。
#cat get_jvmstatus.sh
#!/bin/bash
MAINCLASS=”*Main.class”
scriptPath=/usr/local/etc/scripts
cat $scriptPath/package_path.txt|while read line
do
packageName=$(echo $line|awk ‘{print $1}’)
pgRootPath=$(echo $line|awk ‘{print $2}’)
if [[-d $pgRootPath/tomcat]];then
pid=$(cat $pgRootPath/tomcat/tomcat.pid)
else
mainPath=$(find $pgRootPath -name $MAINCLASS)
appName=$(echo ${mainPath##*classes/}|sed ‘s#/#.#g’|sed ‘s#.class##g’)
pid=$(ps -fC java|grep “$appName”|awk ‘{print $2}’)
fi
javaHome=/usr/local/java/jdk1.8.0
#javaHome=/usr/local/java/latest
#if [[-r $pgRootPath/appconfig]] && [`grep ^JAVA_HOME= $pgRootPath/appconfig|wc -l` == 1] && [`grep ^JAVA_HOME= $pgRootPath/appconfig|grep 8|wc -l` == 1];then
#javaHome=$(grep ^JAVA_HOME= $pgRootPath/appconfig 2>/dev/null|awk -F’=’ ‘{print $2}’)
#javaHome=/usr/local/java/jdk1.8.0
#else
# if [[-r $pgRootPath/webconfig]] && [`grep ^’export JAVA_HOME=’ $pgRootPath/webconfig|wc -l` == 1] && [`grep ^’export JAVA_HOME=’ $pgRootPath/webconfig|grep 8|wc -l` == 1];then
# #javaHome=$(grep ^’export JAVA_HOME=’ $pgRootPath/webconfig 2>/dev/null|awk -F'”‘ ‘{print $2}’)
# javaHome=/usr/local/java/jdk1.8.0
#fi
#fi
#echo ——————————–$pgRootPath
#echo $javaHome
echo ——————————-$pid
sleep 5
#echo -n >$scriptPath/package/$packageName
#$javaHome/bin/jstat -gccapacity $pid > ./package/$packageName 2>/dev/null
#$javaHome/bin/jmap -heap $pid>>./package/$packageName 2>/dev/null
echo gcnew >> $scriptPath/package/$packageName 2>/dev/null
$javaHome/bin/jstat -gcnew $pid >> $scriptPath/package/$packageName 2>/dev/null
echo gcutil >> $scriptPath/package/$packageName 2>/dev/null
$javaHome/bin/jstat -gcutil $pid >> $scriptPath/package/$packageName 2>/dev/null
echo gcnewcapacity >> $scriptPath/package/$packageName 2>/dev/null
$javaHome/bin/jstat -gcnewcapacity $pid >> $scriptPath/package/$packageName 2>/dev/null
echo gccapacity >> $scriptPath/package/$packageName 2>/dev/null
$javaHome/bin/jstat -gccapacity $pid >> $scriptPath/package/$packageName 2>/dev/null
#echo gcold >> $scriptPath/package/$packageName 2>/dev/null
#$javaHome/bin/jstat -gcold $pid >> $scriptPath/package/$packageName 2>/dev/null
echo gc >> $scriptPath/package/$packageName 2>/dev/null
$javaHome/bin/jstat -gc $pid >> $scriptPath/package/$packageName 2>/dev/null
echo class >> $scriptPath/package/$packageName 2>/dev/null
$javaHome/bin/jstat -class $pid >> $scriptPath/package/$packageName 2>/dev/null
echo cpu >> $scriptPath/package/$packageName 2>/dev/null
echo -e “CPU\n$(ps aux|grep $pid|grep -v grep|awk ‘{print $3}’)” >> $scriptPath/package/$packageName 2>/dev/null
echo mem >> $scriptPath/package/$packageName 2>/dev/null
echo -e “MEM\n$(ps aux|grep $pid|grep -v grep|awk ‘{print $6}’)” >> $scriptPath/package/$packageName 2>/dev/null
done
这里面首先是通过获取到程序的根目录,然后我这的 java 程序除了 tomcat 跑的之外,其他的 java 程序都是通过 Main.class 启动的,所以可以获取到 AppName,这样通过 ps 命令就能找到其对应的 PID 了,而如果是 tomcat 启动的进程的话,在程序根目录下面的 tomcat 目录下有一个 tomcat.pid 文件里面有该程序的 PID。后面被注释的那一端代码其实之前是加上去的,那段代码的作用是判断该进程使用的是 JDK7 还是 JDK8 启动的,当初的计划是想着如果是 JDK7 启动的进程就用 JDK7 的 jstat 去获取数据,如果是 JDK8 启动的进程就用 JDK8 的 jstat 去获取数据,后来发现不同版本的 JDK 获取的数据格式不同,于是。。。。。。后悔莫及的把那段代码注释掉了。后面综合公司实际情况考虑,JDK8 的程序用得比较多,JDK7 的程序相对来说比较少,并且慢慢都会向 JDK8 进行转换,所以,权衡利弊之下,之后将 jstat 的 JDK 全部换成了 JDK8,这样的影响就是获取不到 JDK7 的永久代数据。当然,各位有兴趣的话,也可以 JDK7 和 JDK8 同时使用,在过滤输出文件的时候加一个标志位进行判断,当然,我这里暂时没有做这方面的修改。。。毕竟时间有限。。。
第四个脚本,个人感觉写的最烂的一个脚本。。。但是。。。没办法,技术水平有限,各位将就着看吧(捂脸哭)
# cat set_jvmstatus.sh
#!/bin/bash
packageName=$1
key=$2
if [$2 == “S0C” -o $2 == “S0U” -o $2 == “S1C” -o $2 == “S1U” -o $2 == “DSS” -o $2 == “EC” -o $2 == “EU”];then
part=gcnew
elif [$2 == “S0” -o $2 == “S1” -o $2 == “E” -o $2 == “O” -o $2 == “M” -o $2 == “CCS” -o $2 == “YGCT” -o $2 == “FGCT” -o $2 == “GCT”];then
part=gcutil
elif [$2 == “S0CMX” -o $2 == “S1CMX” -o $2 == “ECMX”];then
part=gcnewcapacity
elif [$2 == “NGCMN” -o $2 == “NGCMX” -o $2 == “NGC” -o $2 == “OGCMX” -o $2 == “OGCMN” -o $2 == “OGC” -o $2 == “MCMN” -o $2 == “MCMX” -o $2 == “MC” -o $2 == “CCSMN” -o $2 == “CCSMX” -o $2 == “CCSC” -o $2 == “YGC” -o $2 == “FGC”];then
part=gccapacity
elif [$2 == “MU” -o $2 == “CCSU” -o $2 == “OC” -o $2 == “OU”];then
part=gc
elif [$2 == “Loaded” -o $2 == “Unloaded”];then
part=class
elif [$2 == “CPU”];then
part=cpu
elif [$2 == “MEM”];then
part=mem
else
echo “Error input:”
exit 0
fi
case $2 in
S0C)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $1*1024}’
;;
S0U)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $3*1024}’
;;
S0)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%f\n”, $0}’
;;
S0CMX)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $4*1024}’
;;
S1C)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $2*1024}’
;;
S1U)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $4*1024}’
;;
S1)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%f\n”, $2}’
;;
S1CMX)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $6*1024}’
;;
DSS)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $7*1024}’
;;
EC)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $8*1024}’
;;
EU)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $9*1024}’
;;
ECMX)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $8*1024}’
;;
E)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%f\n”, $3}’
;;
NGCMN)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $1*1024}’
;;
NGCMX)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $2*1024}’
;;
NGC)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $3*1024}’
;;
OC)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $7*1024}’
;;
OU)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $8*1024}’
;;
OGCMX)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $8*1024}’
;;
OGCMN)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $7*1024}’
;;
O)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%f\n”, $4}’
;;
OGC)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $9*1024}’
;;
M)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%f\n”, $5}’
;;
MC)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $13*1024}’
;;
MU)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $10*1024}’
;;
MCMN)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $11*1024}’
;;
MCMX)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $12*1024}’
;;
CCS)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%f\n”, $6}’
;;
CCSC)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $13*1024}’
;;
CCSU)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $12*1024}’
;;
CCSMN)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $14*1024}’
;;
CCSMX)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $15*1024}’
;;
YGC)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $17}’
;;
YGCT)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%f\n”, $8}’
;;
FGC)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $18}’
;;
FGCT)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%f\n”, $10}’
;;
GCT)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%f\n”, $11}’
;;
TT)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $5}’
;;
MTT)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $6}’
;;
Loaded)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $1}’
;;
Unloaded)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $3}’
;;
CPU)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%f\n”, $1}’
;;
MEM)
grep -wA 2 ^”$part” /usr/local/etc/scripts/package/$1|tail -1|awk ‘{printf “%d\n”, $1*1024}’
;;
*)
echo “Error input:”
;;
esac
exit 0
这套脚本没什么讲的,就是重复的进行一些判断,抓数据并输出(注意,之前写的获取的 jstat 参数的值其实是不准确的,获取的值是以 KB 为单位而不是以字节为单位,所以我取完数据后对数据进行成字节为单位了。)
接下来,讲一下这几个脚本该怎么部署。我这里的 zabbix_agentd 是通过 yum 安装的,所以安装在 /usr/local 目录下,配置文件在 /usr/local/etc 目录下,需要在 zabbix_agentd.conf 里面添加下面两行获取数据的 key(注意,添加好后一定要记得重启 zabbix_agentd 进程):
UserParameter=jmx.discovery,/usr/local/etc/scripts/get_jvmlist.sh
UserParameter=jmx.resource[*],/usr/local/etc/scripts/set_jvmstatus.sh $1 $2
然后脚本都放置在 /usr/local/etc/scripts/ 目录下,该目录下的脚本权限如下:
-rwxr-xr-x 1 zabbix zabbix 326 3 月 26 22:29 get_jvmlist.sh
-rwxr-xr-x 1 root root 2956 3 月 28 20:57 get_jvmstatus.sh
-rwxr-xr-x 1 root root 818 3 月 26 22:33 jvm_list.sh
drwxr-xr-x 2 zabbix zabbix 4096 3 月 26 23:05 package
-rw-r–r– 1 zabbix zabbix 1947 3 月 29 11:23 package_path.txt
-rwxr-xr-x 1 zabbix zabbix 5240 3 月 28 20:50 set_jvmstatus.sh
然后需要在 crontab 里面定义 jvm_list.sh 和 get_jvmstatus.sh 脚本的定时任务,我这里定义的如下:
12 * */1 * * * /usr/local/etc/scripts/jvm_list.sh
*/5 * * * * /usr/local/etc/scripts/get_jvmstatus.sh
注意这两个脚本必须要以 root 权限去执行,因为里面涉及到的一些命令只有 root 用户才有权限去执行。
之后可以手动执行脚本去获取数据,看是否能够抓取到相应的数据。
好了,这章的脚本讲完了,下一章,就是怎样通过 zabbix 获取相应的数据了。
第四章:zabbix 获取数据
通过之前的脚本部署,可以在 zabbix_server 上面通过 zabbix_get 命令去检查是否获取到了相应的数据:
# zabbix_get -s xx.xx.xx.xx -k jmx.resource[Abcdefg-1.0.0-rc-bin.tar.gz,MEM]
641036288
我这里可以获取到数据了 (注意 IP 被我注释掉了,为了保护隐私哈,包名也被我刻意修改了,隐私隐私哈)
接下来就可以部署模板了,至于模板我已经做好了,可以直接在附件里面下载。至于模板我制作了一些简单的 key 的值收集,以及图像的展示,至于监控报警值的设置,由于各个公司的环境不一样,需要各位自己根据自己需求自行设置。
Zabbix 模板到Linux 公社资源站下载:
—————————————— 分割线 ——————————————
免费下载地址在 http://linux.linuxidc.com/
用户名与密码都是www.linuxidc.com
具体下载目录在 /2016 年资料 / 9 月 /12 日 /Zabbix 使用自动发现功能监控服务器各 JVM 进程状态 /
下载方法见 http://www.linuxidc.com/Linux/2013-07/87684.htm
—————————————— 分割线 ——————————————
本文永久更新链接地址:http://www.linuxidc.com/Linux/2016-09/135139.htm