Tomcat意外宕機分析


  之前在網上看過一篇文章,是講Tomcat進程意外退出的,我看完感覺好奇,自己也測試了下,果然是有這種問題,所以自己也借此總結一下。

  先簡單說下測試過程,先創建一個web服務啟動 test.sh,內容如下:

#!/bin/bash
cd /usr/software/tomcat/apache-tomcat-7.0.81/bin/
./catalina.sh start
tail -f /usr/software/tomcat/apache-tomcat-7.0.81/logs/catalina.out

  然后啟動該腳本,服務起來了,可以正常訪問。

  tomcat啟動之后,當前shell進程並沒有退出,而是掛住在tail進程,往終端輸出日志內容。這種情況下:

  1)、如果我先直接關掉ssh窗口后,Java進程會退出,服務不可用。

  2)、而我如果先 用ctrl-c終止test.sh進程,然后再關閉ssh終端的話,這時Java進程不會退出。服務沒有受到影響,仍然可用。

  3)、下面我又把最后tail這一行去掉,發現直接關掉ssh終端窗口,Java進程也不會退出,服務不受影響。

#!/bin/bash
cd /usr/software/tomcat/apache-tomcat-7.0.81/bin/
./catalina.sh start

  4)、最后我再次恢復tail這一行,然后在開頭再加上 set -m, 直接關掉ssh窗口Java進程同樣也不會退出,服務同樣也不受影響。

#!/bin/bash
set -m
cd /usr/software/tomcat/apache-tomcat-7.0.81/bin/
./catalina.sh start
tail -f /usr/software/tomcat/apache-tomcat-7.0.81/logs/catalina.out

  到這里是不是有點暈了,不急,待我慢慢道出原因來。

 

  這是一個有趣的現象,catalina.sh start方式啟動的tomcat會把java進程掛到init(進程id為1)的父進程下,已經與當前test.sh進程脫離了父子關系,也與ssh進程沒有關系,為什么關閉ssh終端窗口會導致java進程退出?

  經過測試,有發現:

  a) 用 ctrl-c 終止當前test.sh進程時,系統events進程向 java 和 tail 兩個進程發送了SIGINT 信號

  b) 關閉ssh終端窗口時,sshd向下游進程發送SIGHUP, java進程也會收到。

  后來通過google后了解到: shell在非交互模式下對后台進程處理SIGINT信號時設置的是IGNORE

  交互模式與非交互模式對作業控制(job control)默認方式不同:

  I)在交互模式下,因為作業控制的需要shell不會對后台進程處理SIGINT信號設置為忽略。因此這樣的話,父進程會把收到的鍵盤事件比如ctrl-c之類的SIGINT傳播給進程組中的每個成員(假設后台進程也是父進程組的成員),那么終端隨意ctrl-c就可能導致所有的后台進程退出,顯然這樣是不合理的。因此,在交互模式下的后台進程會設置一個自己的進程組ID。

  II)而非交互模式下,通常是不需要作業控制的,所以作業控制在非交互模式下默認也是關閉的(當然也可以在腳本里通過選項set -m打開作業控制選項)。不開啟作業控制的話,腳本里的后台進程可以通過設置忽略SIGINT信號來避免父進程對組中成員的傳播,因為對它來說這個信號已經沒有意義。

  在非交互模式下,shell對java進程設置了SIGINTSIGQUIT信號設置了忽略,但並沒有對SIGHUP信號設為忽略,回頭看上面說的,直接關閉ssh終端窗口時,sshd向下游進程發送SIGHUP, java進程也會收到。

  再看一下當時的進程層級:

 

  |-sshd(1622)-+-sshd(11681)---sshd(11699)---bash(11700)---test.sh(13285)---tail(13299)

  

  sshd把SIGHUP傳遞給bash進程后,bash會把SIGHUP傳遞給它的子進程,並且對於其子進程test.sh,bash還會對test.sh的進程組里的成員都傳播一遍SIGHUP。因為java后台進程從父進程catalina.sh(又是從其父進程test.sh)繼承的pgid,所以java進程仍屬於test.sh進程組里的成員,收到SIGHUP后退出。

  如果我們在test.sh里設置開啟作業控制的話,就不會讓java進程退出了

#!/bin/bash
set -m
cd /usr/software/tomcat/apache-tomcat-7.0.81/bin/
./catalina.sh start
tail -f /usr/software/tomcat/apache-tomcat-7.0.81/logs/catalina.out

  此時java后台進程繼承父進程catalina.sh的pgid,而catalina.sh不再使用test.sh的進程組,而是自己的pid作為pgid,catalina.sh進程在執行完退出后,java進程掛到了init下,java與test.sh進程就完全脫離關系了,bash也不會再向它發送信號。

  

  注: 如果把tail 打印的這一行去掉,為什么Java進程也不會退出,我猜原因應該是,如果沒有tail的話, Java進程就不是他們的子進程了,自然怎么操作都不會受影響。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM