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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
============================================
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抓到的這些參數了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
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進程的狀態信息

好了,簡單介紹了上面幾個腳本的功能,下面我們列出這幾個腳本的實際內容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    #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對應的文件中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    #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狀態,並緩存到文件中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
    #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同時使用,在過濾輸出文件的時候加一個標誌位進行判斷,當然,我這裏暫時沒有做這方面的修改。。。畢竟時間有限。。。

第四個腳本,個人感覺寫的最爛的一個腳本。。。但是。。。沒辦法,技術水平有限,各位將就着看吧(捂臉哭)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
    # 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進程):

1
2
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/目錄下,該目錄下的腳本權限如下:

1
2
3
4
5
6
 -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
2
* */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://xianglinhu.blog.51cto.com/5787032/1757930


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