前言java
==========web
为何须要作服务器jvm自动发现的监控呢?这个事情主要有两点缘由:shell
1.zabbix默认监控jvm状态是使用jmx中转进行监控的,监控效率比较低下json
2.zabbix使用jmx监控jvm的时候因为一个主机上的键值不能重复,也就致使了一台主机上只能监控一个jvm实例缓存
以上两点缘由致使zabbix经过jmx监控jvm的实现不是很理想,加上最近老大要求收集服务器上面跑的全部java应用的信息,因而本身琢磨了下,仍是本身动手,丰衣足食。利用了周末的时间,经过使用shell脚本+java工具jstat+zabbix实现监控主机上多jvm实例的功能。tomcat
第一章:概念的理解bash
首先,既然要监控jvm状态,那就必需要了解jvm里面的信息,楼主经过搜索资料加自动脑补,把网上的资料取其精华,去其糟粕,整理了一下。JVM中的内存分类分为堆内存和非堆内存,堆内存是给实际应用使用的,非堆内存是给jvm容器使用的。咱们主要关心的是堆内存这块。在堆内存里面,给内存分为以下几块:服务器
1.Young代(年轻代)并发
2.Old代(老年代)app
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抓到的这些参数了。
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脚本的定时任务,我这里定义的以下:
* */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的值收集,以及图像的展现,至于监控报警值的设置,因为各个公司的环境不同,须要各位本身根据本身需求自行设置。
后记:
终于,写完了,写了一早上的博客,心好累,一早上没怎么作事,会不会被老大打死?