前言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的值收集,以及圖像的展現,至於監控報警值的設置,因爲各個公司的環境不同,須要各位本身根據本身需求自行設置。
後記:
終於,寫完了,寫了一早上的博客,心好累,一早上沒怎麼作事,會不會被老大打死?