一次訊號量引發的tomcat異常退出
近期在玩大資料。有個朋友找過來,說他線上的tomcat會莫名其妙的退出,表示非常苦惱,請我幫看看。每次他發現退出了,都通過騰訊雲的WEB控制檯登入,啟動tomcat。
本著助人為樂(shao kao hao chi)的精神,我連上去開始分析。首先肯定是看tomcat的日誌,看看有沒有記錄到相關資訊,是什麼途徑退出的。
從日誌上看,tomcat收到了退出請求,並按照要求關閉容器。那麼是否可以認為是有人執行了shutdown.sh呢?並不能。執行了shutdown指令碼的關閉日誌是這樣的。
與其相關的tomcat原始碼截圖如下。截圖左側有行號。
tomcat啟動時,設定await,等待關閉指令進入。org\apache\catalina\startup\Bootstrap.java
catalinaDaemon的定義如下。
org\apache\catalina\startup\Catalina.java
具體例項化時,會將介面Server的例項指向StandardServer。類路徑如下。
org\apache\catalina\Server.java
org\apache\catalina\core\StandardServer.java
而StandServer中的輸出相關日誌的原始碼如下:
讀取的配置檔案為org\apache\catalina\core\LocalStrings.properties
當tomcat收到正經的關閉指令時,會輸出此日誌,說明是收到指令關閉容器。
正經的指令關閉容器,相關程式碼如下。
那麼,現在的證據說明,這個tomcat不是通過SHUTDOWN報文關閉的。而且,從下圖來看,也頗能說明這個SHUTDOWN指令不是這麼容易發成功的。
那麼現在可能性最大的辦法就是通過KILL指令來操作。執行bash指令碼需要登入機器,那麼從wtmp、utmp查詢一下這個時間點的登入記錄呢?
下面是IPIP的結果。
換言之,23日早上tomcat異常退出的時候,有一個來自騰訊雲的BGP機房的地址也巧合的斷開了會話。而我這個朋友的機器就放在騰訊雲。有點奇怪是嗎?
繼續追查,連續追溯幾天的tomcat日誌,比對utmp、wtmp結果,再比對IPIP結果,都是如此。來自騰訊雲BGP機房的會話斷開,tomcat同一時間點退出。精確到秒級。連續多天出現很多次,說明tomcat退出和WEB會話退出是具備因果關係的。
經過詢問,朋友確認他是習慣於使用WEB控制檯的方式登入伺服器,啟動了tomcat以後就丟在一邊,開始除錯介面了。那麼有什麼可能會導致這樣的因果關係出現?這就要說到Linux系統的一個歷史悠久的程序間通訊的機制——訊號量。
具體訊號量是什麼,請自行查詢相關資料瞭解學習。針對本次問題,可以簡單的理解為程序間通訊的一種機制。
程序A需要程序B做點事,而程序間的記憶體區域某種意義上說是互不可見的。這個時候就需要通過訊號量來完成。程序A可以按照預先定義的訊號量規範向程序B發出訊號量,當程序B收到後,根據具體訊號量的值決定處理邏輯。具體訊號量清單,可以在命令列通過如下命令查詢。命令中均為字母,沒有數字1。
這其中最常見的就是9,SIGKILL。當程序收到此訊號量時,會被KILL掉。此訊號量由作業系統處理,應用不能處理。在vista之前的windows系統中,是有辦法滲透到核心中的。此時可以攔截類似WM_CLOSE之類的訊息,讓某個程式無法關閉。到了win7、win10時代,已經不能使用此類技巧了。
此外,我們熟悉的CTRL + C操作,發出的是SIGINT。有些場景下,我們需要通知程式優雅的退出,此時可以發出SIGQUIT,也就是kill -3。
那麼WEB控制檯會話斷開,會發出什麼訊號量呢?我們來試試就知道了。Java雖然說不能作業系統底層,但是sun.misc包有驚喜哦。程式碼如下。
如圖所示,這段程式碼會在收到訊號量時輸出執行緒名稱,訊號量名稱,並翻譯成具體的數字。隨後,在main函式中,我“註冊”了HUP、INT、ABRT、TERM四種訊號量。註冊四種是因為不清楚具體會發什麼出來,索性有可能的都搞起來。
編譯,打包。此處有一個問題需要注意,由於訊號量屬於作業系統底層機制,每個不同作業系統所支援的訊號量是不同的,JVM中通過private static native int findSignal(String paramString)提供支援。native方法涉及具體VM實現,不貼程式碼了。不過很容易想到的是,windows和linux當然不同。所以此處就要在上位機編寫,下位機除錯了。windows底下執行報錯可不要慌張哦。
接下來的事情就簡單了。把程式上傳伺服器,通過WEB控制檯登入伺服器,將執行結果重定向到文字檔案中,然後靜待控制檯超時。結果如下。
結果不用再分析了,WEB控制檯會在退出時發出SIGHUP,相當於kill -1。而tomcat在收到SIGHUP會怎麼操作呢?小夥伴們可以試試看kill -1 pid,再看看日誌,就明白了。
解決方案其實也簡單。SIGHUP是HANG UP的意思,可以用nohup xx.sh &來徹底遮蔽SIGHUP和SIGINT。另外,經過測試發現,通過單擊SecureCRT的tab頁右側的×也可以觸發訊號量,而直接logout或者點選單上的紅叉則並不會觸發。
感興趣的小夥伴可以把這個程式擴充套件一下,測測看。說不定你的異常退出問題也能迎刃而解呢。
此問題的解決離不開上海中通的劉建剛同學,特此致