Zabbix使用自動發現功能監控服務器各JVM進程狀態


前言

==========
爲什麼需要做服務器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的值收集,以及圖像的展示,至於監控報警值的設置,由於各個公司的環境不一樣,需要各位自己根據自己需求自行設置。


轉載至:http://www.linuxidc.com/Linux/2016-09/135139.htm

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章