JMX,Jstatd做好JVM應用上線的最後一層保障

一個成功的java專案標準並不僅僅是業務功能實現,但是縱觀國內,很多專案組在前期專案開發設計中只考慮了業務功能,沒有考慮專案後期維護的監控設計。沒有完善的監控運維設計,專案存活的壽命應該也不長吧?好的專案能夠吸引人留下來,並不斷強化專案的功能最佳化每一處程式碼,壞的專案只會逼死人,不斷地增加齷齪程式碼以至於根本無法維護。

當然從公司來說,業務的首要實現是公司能夠賺錢的有效保障,公司賺不了錢了,寫得再好的程式碼也只能靜靜地躺在硬碟中。我想一個負責的開發人員不僅要能重視業務功能的實現,還能保證在專案上線運維中針對突發情況做到監控。

我理解的監控

我理解的監控分兩種,一種是運維的監控-監控整個叢集的各項資源的使用情況以及各個服務的存活情況,另一種是開發的監控-監控程式碼問題導致的執行緒死鎖,OOM等,以及業務訊息的歷史可回溯。

我是一名開發,這裡主要講講我的心得,開發中的監控。如何減少開發人員不必要的加班。

程式碼異常監控

應用程式碼在面對線上各種請求時,經常會發生死鎖,OOM等問題。這個時候我們應該如何去檢視呢?如果我們不想連上遠端伺服器,可以透過本地的一些視覺化工具連線遠端程式,檢視遠端程式的執行緒,CPU,GC,堆記憶體等使用情況。

遠端主機配置jmx

這裡只是演示JMX的監控功能,JMX還有動態修改bean屬性等功能不在這一篇文章講解。

修改密碼,找到配置檔案$JAVA_HOME/jre/lib/management/jmxremote。password。template,複製一份並改名為jmxremote。password,然後修改只讀許可權並編輯jmxremote。passwrod,取消以下兩行註釋:

#monitorRole QED#controlRole R&D

修改要啟動的java程式啟動引數(JVM_OPTS)。

開啟tomcat的bin目錄下的catalina。sh,加入以下內容**(非tomcat程式也類似)**

JAVA_OPTS=“$JAVA_OPTS -Djava。rmi。server。hostname=192。168。19。131 -Dcom。sun。management。jmxremote。port=18999 -Dcom。sun。management。jmxremote。ssl=false -Dcom。sun。management。jmxremote。authenticate=false”

引數authenticate表示是否需要密碼認證,賦值為true就會使用jmxremote。password設定的密碼。

修改檔案許可權

監控的程式是由哪個使用者啟動,則把jmxremote。password檔案的許可權改為這個使用者的只讀許可權,

否則啟動程式會報錯:Error: Password file read access must be restricted。

這些在jmxremote。password裡的註釋都有說明。

比如,如果你是用intsmaze使用者啟動java程式

chown intsmaze jmxremote。passwordchmod 400 jmxremote。password

啟動jvisualvm

先啟動待監控的程式

sh startup。sh

左邊欄,右鍵“遠端”>>“新增遠端主機”

JMX,Jstatd做好JVM應用上線的最後一層保障

左側欄,右鍵剛才新增的遠端主機>>“新增jmx連結”,使用配置的埠

JMX,Jstatd做好JVM應用上線的最後一層保障

JMX,Jstatd做好JVM應用上線的最後一層保障

如果我們不配置JVM_OPTS引數,那麼我們在本地使用javaVisualVM是無法訪問遠端伺服器上的tomcat服務的狀況,要想知道遠端伺服器的狀況就必須使用CRT等工具連上伺服器使用linux命令去檢視程式的執行情況。

監控伺服器上的java程式

在java -cp 命令中加入如下引數即可

java -Dcom。sun。management。jmxremote -Dcom。sun。management。jmxremote。ssl=false -Dcom。sun。management。jmxremote。authenticate=false -Dcom。sun。management。jmxremote。port=22222 -cp jmx。jar cn。intsmaze。thread。TestDeadThread

TestDeadThread類如下

public class TestDeadThread implements Runnable { int a, b; public TestDeadThread(int a, int b) { this。a = a; this。b = b; } public void run() { synchronized (Integer。valueOf(a)) { synchronized (Integer。valueOf(b)) { System。out。println(a + b); } } } public static void main(String[] args) throws InterruptedException { Thread。sleep(3000); for (int i = 0; i < 100; i++) { new Thread(new TestDeadThread(1, 2))。start(); new Thread(new TestDeadThread(2, 1))。start(); } }}

JvisiualVM透過JMX的方式連線到遠端伺服器上的JVM,此時能獲取到JVM的基本資訊(啟動引數、系統屬性)、CPU使用情況、堆記憶體整體情況以及執行緒的整體情況等。但如果想透過Visual GC外掛進一步瞭解堆內各區的情況的話,就會發現外掛此時並不工作。

JMX,Jstatd做好JVM應用上線的最後一層保障

Visual GC外掛不工作,是因為此外掛使用的協議是RMI,因此需要使用下面的jstatd方式進行連線。

jstatd 連線到遠端JVM

JVM jstat Daemon:守護程序,一個RMI伺服器程式,用於監控本地所有JVM從建立開始直到銷燬整個過程中的資源使用情況,同時提供介面給監控工具(如這裡的VisualVM),讓工具能連線到本機所有的JVM。

啟動jstatd服務

${java_home}/bin目錄下啟動jstatd服務

[intsmaze@centos-Reall-131 bin]。/jstatdCould not create remote objectaccess denied (“java。util。PropertyPermission” “java。rmi。server。ignoreSubClasses” “write”)java。security。AccessControlException: access denied (“java。util。PropertyPermission” “java。rmi。server。ignoreSubClasses” “write”) at java。security。AccessControlContext。checkPermission(AccessControlContext。java:472) at java。security。AccessController。checkPermission(AccessController。java:884) at java。lang。SecurityManager。checkPermission(SecurityManager。java:549) at java。lang。System。setProperty(System。java:792) at sun。tools。jstatd。Jstatd。main(Jstatd。java:139)

由於jstatd server沒有提供任何對遠端client端的認證,客戶端程式獲取到本地當前使用者的所有JVM資訊後可能存在安全隱患,所以jstatd要求啟動之前必須指定本地安全策略,否則jstatd程序無法啟動,丟擲上面錯誤。

建立安全策略檔案

在需要被監控的遠端主機建立一個安全策略檔案,比如儲存為/home/intsmaze/jdk1。8。0_144/bin/jstatd-all。policy,內容如下:

grant codebase “file:/home/intsmaze/jdk1。8。0_144/lib/tools。jar” {permission java。security。AllPermission;};

啟動jjstatd帶引數

透過如下命令可以成功啟動jstatd server

。/jstatd -J-Djava。security。policy=/home/intsmaze/jdk1。8。0_144/bin/jstatd-all。policy -J-Djava。rmi。server。logCalls=true。/jstatd -J-Djava。security。policy=/home/intsmaze/jdk1。8。0_144/bin/jstatd-all。policy &

向透過jstatd命令啟動的JVM(Main class:sun。tools。jstatd。Jstatd)傳遞引數,比如-J-Xms48m指定了Jstatd這個JVM的初始堆記憶體為48MB

右鍵選擇建立jstatd連線

JMX,Jstatd做好JVM應用上線的最後一層保障

對應的遠端主機節點下會自動列出所有執行的JVM

JMX,Jstatd做好JVM應用上線的最後一層保障

JMX連線與JStatD連線的區別

JMX:使用JMX需要遠端JVM在啟動的時候開啟遠端訪問支援,設定JMX埠等,每一個JMX連線一個遠端JVM。

JStatD:使用jstatd連線方式時,需要在遠端主機上建立安全策略檔案然後啟動jstatd程序,並且此程序需要一直保持執行狀態,客戶端可以看到遠端主機上當前使用者的所有JVM的資訊,即只要建立一個jstatd連線。

linux命令監控jvm程式

如果我們不配置JMX和jstatd,那麼我們無法使用jvisiualVM去監控遠端JVM程式,要知道程式的執行狀態我們必須連上伺服器去檢視。

top命令檢視各程序CPU佔用率

[intsmaze@centos-Reall-131 ~]$ toptop - 13:04:07 up 3 min, 2 users, load average: 0。00, 0。01, 0。00Tasks: 104 total, 1 running, 103 sleeping, 0 stopped, 0 zombieCpu(s): 0。0%us, 0。2%sy, 0。0%ni, 99。8%id, 0。0%wa, 0。0%hi, 0。0%si, 0。0%stMem: 2086348k total, 224720k used, 1861628k free, 37484k buffersSwap: 2064376k total, 0k used, 2064376k free, 91204k cached PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 385 root 20 0 0 0 0 S 0。3 0。0 0:00。02 flush-8:0 2211 intsmaze 20 0 858m 25m 9448 S 0。3 1。2 0:00。87 java

第一行:load average: 0。41, 0。45, 0。43 系統負載,即任務佇列的平均長度。1分鐘前、5分鐘前、15分鐘前平均負載

第二行:Tasks: 141 total 程序總數,0 zombie 殭屍程序數

第三行為cpu資訊

6。1% us 使用者空間佔用CPU百分比

1。5% sy 核心空間佔用CPU百分比

0。0% ni 使用者程序空間內改變過優先順序的程序佔用CPU百分比

92。2% id 空閒CPU百分比

0。0% wa 等待輸入輸出的CPU時間百分比

0。0% hi 硬體中斷

0。0% si 軟體中斷

0。0%st 實時

第四、五行為記憶體資訊。

Mem: 191272k total 物理記憶體總量

22052k buffers 用作核心快取的記憶體量

Swap: 192772k total 交換區總量

123988k cached 緩衝的交換區總量

程序中每個執行緒佔用cpu情況

[intsmaze@centos-Reall-131 ~]$ top -Hp 2461 PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 2462 intsmaze 20 0 870m 25m 9416 S 30。0 1。2 0:00。28 java 2463 intsmaze 20 0 870m 25m 9416 S 0。0 1。2 0:00。00 java 2464 intsmaze 20 0 870m 25m 9416 S 0。0 1。2 0:00。00 java

定位執行緒的執行情況

Jstack是JDK自帶的命令列工具,主要用於執行緒Dump分析,能得到執行java程式的java stack和native stack的資訊,可以輕鬆得知當前執行緒的執行情況。

jstack -l [pid]檢視所有執行緒資訊

jstack -l 2238 > intsmaze。log[intsmaze@centos-Reall-131 ~]$ jstack -l 2461“Thread-200”: at cn。intsmaze。thread。TestDeadThread。run(TestDeadThread。java:29) - waiting to lock <0x9d62a3a0> (a java。lang。Integer) at java。lang。Thread。run(Thread。java:748)“Thread-10”: at cn。intsmaze。thread。TestDeadThread。run(TestDeadThread。java:30) - waiting to lock <0x9d62a390> (a java。lang。Integer) - locked <0x9d62a3a0> (a java。lang。Integer) at java。lang。Thread。run(Thread。java:748)

jstack命令生成的thread dump資訊包含了JVM中所有存活的執行緒,為了分析指定執行緒,必須找出對應執行緒的呼叫棧,應該如何找?

jstack -l [pid] | grep 16進位制

top -Hp [pid] 中獲取到了佔用cpu資源較高的執行緒pid,將該pid轉成16進位制的值,在thread dump中每個執行緒都有一個nid,找到對應的nid即可。

得到2462 的十六進位制值···[intsmaze@centos-Reall-131 ~]$ printf “%x\n” 2462 99e···jstack -l 21711 | grep 99e“PollIntervalRetrySchedulerThread” prio=10 tid=0x00007f950043e000 nid=0x99e in Object。wait()

在nid=0x99e 的執行緒呼叫棧中,CPU消耗在PollIntervalRetrySchedulerThread這個類的Object。wait(),然後去觀察自己寫的業務程式碼。