如何讓Java應用成爲殺不死的小強?(中篇)

【這是一猿小講的第 48 篇原創分享】服務器


各位坐穩扶好,咱們要開車了。不過在開車以前,咱們仍是例行回顧一下上期分享的要點。socket


上期咱們拋了一個磚:「如何實現 Java 應用進程的狀態監控,若是被監控的進程 down 掉,是否有機制能啓動起來?」並結合 Resin 應用服務器背後啓動的進程,更詳細的闡述了一下問題。函數


咱們依然不考慮本身怎麼去實現,而是先看看 Resin 這款技術輪子,是否是按照咱們的猜測設計的呢?是否是能夠模仿一二?ui


好了,熟讀唐詩三百首,不會做詩也會吟,抱着疑問,咱們再深刻一次 Resin 的源碼,若是不感興趣,或者腦仁疼,那建議跳到下篇直接擼碼實戰。命令行


1.

咱們從 ResinBoot 的 main 函數開始,一步一步來看看怎麼實現,目標是提取裏面的關鍵核心思想,細枝末節莫糾結。線程


ResinBoot 的 main 函數是 Resin 應用服務器入口,大概流程以下:設計

建立 ResinBoot 實例 boot;3d

而後根據傳入的 args 參數獲取對應的 XxCommand 對象;日誌

接着調用 ResinBoot 的 start 函數,完成服務的啓動;orm

最後退出 ResinBoot 的進程。


那接下來不妨再深刻看看建立 ResinBoot 對象時,構造方法中都幹了啥?


發現 ResinBoot 建立對象時,構造方法中建立了 WatchdogArgs 對象,WatchdogArgs 裏面到底又作了什麼呢?


發現 WatchdogArgs 靜態代碼塊中提早預製了一堆命令執行的對象實例,其中就包括 StartCommand,再回頭看看它的構造方法,會發現會進行解析我們 main 函數傳入的 args 入參,而後根據入參獲取對應的命令執行對象。


其中格式化命令行入參的方法 parseCommandLine 會匹配一堆預製的參數,實在匹配不到就從靜態的 _commandMap 中去匹配對應的命令執行對象,固然我們傳入的參數是 start,因此會匹配成功 StartCommand。


那麼咱們再回頭看看 ResinBoot 的 main 函數,發現又調用了 ResinBoot 的 start 方法。

那咱們也一塊兒看看 ResinBoot 的 start 方法作了些什麼操做?發現方法中獲取對應的 command,而後調用 command.doCommand() 方法。


咱們知道此時的 command 爲 StartCommand,那 StartCommand 中的 doCommand() 到底作了什麼事情呢?會發現 doCommand 方法中調用了 WatchdogClient 的 startWatchdog 的方法。


那咱們看看 startWatchdog 方法中又作了哪些事情?重點要來了,重點要來了,重點要來了。發現調用了 launchManager 方法,那 launchManager 又是幹什麼的呢?

仔細深刻會發現,launchManager 方法主要用 ProcessBuilder 來實現啓動 WatchdogManager 的進程。


而且第 539 行有一句日誌輸出,和我們在控制檯輸入 ./resin.sh start 命令啓動 Resin 服務時的日誌不謀而合。


至此,ResinBoot 經過 ProcessBuilder.start() 成功觸發啓動 WatchdogManager 進程,也就是我們猜想的大總管進程,哪丫環進程是否也存在呢?丫環進程又是怎麼啓動的呢?


2.

此時咱們跟隨源碼設計的腳步,繼續向下刨一刨 WatchdogManager,發現 WatchdogManager 程序入口 main 函數中,根據傳入參數構建了 WatchdogManager 對象,並經過參數獲取對應的 XxCommand(似曾相識的感受),而後經過 XxCommand 調用 doWatchdogStart 函數完成服務的啓動。


再深刻去跟蹤,發現 doWatchdogStart 方法中會調用 WatchdogManager 的 startServer 方法。


那 startServer 方法中又幹了什麼事情呢?很顯然是經過配置找到要啓動的子服務,而後調用重載的 startServer 方法。


接着就開始調用 WatchdogChild 的 start 方法,方法幹了什麼事情呢?


WatchdogChild 的 start 方法中建立了 WatchdogChildTask 任務對象,並調用 start 方法完成任務啓動。


其中 WatchdogChildTask 的 start 方法很簡單,就是起了一個線程開始跑任務。


重點再一次來臨,會發現線程體中建立了一個 WatchdogChildProcess 對象實例,接着調用對象的 run 方法。此處就是保證了 Resin 應用爲何一直殺不死的緣由,有個循環調度,一旦子進程有問題,當即再次進行建立子進程。


咱們看看 run 方法的實現,會發現建立了一個 socket 用於通信;而後把端口傳入 createProcess 方法構建 Resin 進程對象。


接着會發現 WatchdogProcess 的建立進程的方法 createProcess 中定義要啓動的類爲 com.caucho.server.resin.Resin;而後封裝一系列的參數;緊接着用 ProcessBuilder 完成 Resin 進程的啓動。


而後 connectToChild 方法主要用於等待子進程的鏈接。這不就是大總管開闢的實時通信的端口麼!


至此,咱們知道 WatchdogManager 會建立 WatchdogTask 任務,用於建立WatchdogChildProcess 對象,此對象會觸發啓動 Resin 進程,並提供 socket 通信服務。


3.

那 Resin 進程又幹了啥?咱們依然從 main 函數開始看起。一目瞭然根據傳入參數建立 Resin 實例,而後重點關注一下 waitForExit,這個是否是和我們猜想的丫環進程與大總管進程通信不上就退出,是否是這麼回事呢?


而後深刻 Resin 的 waitForExit 方法發現經過 pingSocket 參數構建 ResinWaitForExitService,用於啓動 ResinActor(這個 Actor 模型前期的分享我們提過一嘴),而後調用 waitForExit 方法。


到這大概也就到了最後,根據藍色框中的日誌輸出約莫也就是那麼回事兒。


...... 此處省略一萬圖(不刨了,點到爲止)......


至此,丫環進程 Resin 也啓動完畢了,而且與父進程創建了實時通信。源碼看了七七八八,細枝末節沒細談,主要是能明白梗概,梳理個大體脈略,能大體清晰 Resin 服務啓動時啓動了 ResinBoot 進程、WatchdogManager 進程、Resin 進程,當進程啓動完成後 ResinBoot 進程就正常退出了,因此當咱們用 jps 命令看時,就發現只有 WatchdogManager、Resin 兩個進程啦,其中用到的核心技術爲 ProcessBuilder、Socket 通信。


好了,能堅持看到這兒的,那絕對都是鐵粉,但願我不是一人在飲酒醉,獨醉不如衆醉,獨樂樂不如衆樂樂,但願這期的分享能幫你打通任督二脈,之後若是真用到時,不妨以本文做爲參考,說不定會有點價值。


下期咱們將站在 Resin 的肩膀上進行實戰,請各位期待。

推薦閱讀:

老技術新談,Java應用監控利器JMX(1)

老技術新談,Java應用監控利器JMX(2)

老技術新談,Java應用監控利器JMX(3)

一篇文章講透線上應用監控

相關文章
相關標籤/搜索