使用jsvc啓動java進程

使用jsvc啓動java進程

apache commons daemon,apache的一個子項目。
它的說明文檔很簡單,它的實際用法也是。

推薦直接去官網查看它的文檔。
項目地址爲:
https://commons.apache.org/proper/commons-daemon/index.html

一、啓動、關閉java進程時遇到的一些需求

在啓動或是關閉java進程時,有時會對這些操作和進程運行狀態信息提出一些要求。

1. 在查看java進程信息時,希望能隱藏部分classpath。

如果引用的jar文件比較多,那麼進程信息中顯示的classpath就很長,這個對查看進程信息就很不友好,尤其是在一次要查看多個java進程的情況下。

2. 有時希望能以類似同步阻塞調用的方式來啓動和關閉java進程。

只有進程在完全初始化或退出後,執行調用的腳本或命令才返回。

二、相關概念

commons-daemon可以看成是一個獨立的jar文件,需要引入到項目的classpath中來配合使用。
jsvc是一個可以在類Unix上運行的可執行文件。

實際使用時,需要聯合commons-daemon和jsvc一起使用。
通過它們,可以使在類Unix系統上運行java進程更加容易。

比如下面的命令,使用jsvc來啓動一個java進程。

jsvc -user xxx -cwd xxx -procname xxx -pidfile xxx -cp xxx ... JVM_OPTS APP_MAIN_CLASS

可以看出它和普通的java命令類似,但是多了一些前面的參數配置。

三、初識jsvc

大約在2013年或更早的時候,項目使用jsvc來啓動java進程。
當初不知道是誰率先引入的jsvc,也不知道他引入jsvc是爲了解決什麼樣的問題,反正這種方式就這麼被傳承下來了。

但從那時起,使用jsvc的體驗就不太好。
因爲每次啓動時會產生兩個進程,每個進程的信息中都包含了大量的classpath信息。
而且沒有用到jsvc的其它功能,因此也就沒有感覺到使用jsvc帶來的優勢。

新人可能會對這兩個進程產生疑惑,到底哪個是真正的java進程。
而且每次使用ps查看進程時,會發現進程信息佔據了大部分的屏幕空間。

比如下面的這個截圖。
雖然只查看了某一個具體的網關進程信息,但是大部分屏幕空間都被這些信息佔據了,更不用說當要同時查找多個不同功能的進程時所造成的困擾。
沒有隱藏classpath
如果是使用jsvc方式啓動的java進程,在使用jps -l時,是看不到啓動類的fqn或jar的完整路徑的。

四、jsvc基本使用

先說明一下,本人使用的類Unix系統都是Linux,比如CentOS、Ubuntu,沒有實際使用過Unix系統。

1. 在Linux上安裝jsvc命令。

jsvc命令需要通過手動編譯源碼來構建,官網沒有提供現成的下載文件來直接使用。

然而jsvc的構建過程超級簡單。

  1. 下載commons-daemon-1.2.2-src.tar.gz,解壓後進入src/native/unix。
  2. 運行如下命令(文檔頁上也有這些步驟):
./configure --with-java=dir         #設置屬性
make                                #生成jsvc命令
sudo cp jsvc /usr/bin/              #將jsvc拷貝到某個path目錄下

2. 在項目中引入commons-daemon。

  1. 在項目中引入jar。
  2. 啓動類實現Daemon接口;或是啓動類不實現Daemon接口,但是擁有Daemon接口指定的4個方法。

3. 編寫相應的使用腳本

最終使用如下方式調用jsvc命令。
APP_MAIN_CLASS就是實現了Daemon接口的類,或是有Daemon接口的4個方法的類。

啓動java進程:
jsvc -cwd xxx -user xxx -procname -xxx -pidfile xxx … -cp JVM_OPTS APP_MAIN_CLASS

關閉java進程:
jsvc -stop -pidfile xxx APP_MAIN_CLASS

五、jsvc進階

這麼多年來,一直沒有理解爲什麼會選擇使用jsvc來啓動java進程。
目前所在的項目組,也是使用了jsvc,然而也沒有用好它,文章中的截圖就是來自於這個項目組的一個進程 - 網關。
他們雖然引入了jsvc,但是沒有使用到jsvc提供的一些好用的功能,反而造成了一些困擾。

後來自己在尋求簡化classpath信息的過程中,偶然使用-debug選項輸出了很多調試信息。
根據這些信息猜測,jsvc應該是提供了某些核心功能。
於是去官網看了一下它的文檔和api,寫一些測試代碼試了一下,發現了一些從未有使用過的功能。

1. 隱藏classpath信息。

使用jsvc命令啓動java進程後,會產生兩個進程,如果java項目本身引入的jar文件比較多,這些進程信息就會比較長。

如果使用nohupjava &這兩種方式啓動java進程,很多人都會選擇在打包階段將引入jar的classpath信息放到META-INF/MANIFEST.MF文件中,這樣就可以隱藏這些classpath。

見過好幾個項目使用jsvc(包括現在的),但是它們都沒有能解決這個問題。
其實在使用jsvc這種方式中,也可以隱藏掉這些classpath,也是將classpath寫到META-INF/MANIFEST.MF文件中。

這是隱藏classpath後的進程信息。
隱藏classpath後的進程信息

2. 只有在java應用初始化完成後,執行調用的腳本或命令才返回(類似於同步阻塞調用方式)。

這種需求也是很常見的,比如腳本在成功啓動java進程後,再做一些其它操作,這兩個動作是有先後依賴關係的。
之前就嘗試在java代碼中實現這樣的功能,但是沒有成功。

如果單純的使用nohupjava &這兩種方式,就不能實現這樣的效果。

jsvc可以通過-wait選項來滿足這個需求。
如果不使用-wait選項,jsvc命令會立即返回(類似異步調用),也就沒法知道java進程是否會初始化成功。
如果使用-wait選項,當start方法耗時超過wait指定的耗時後,jsvc命令纔會返回。

3. 只有在java進程退出後,執行的腳本或命令才返回。

jsvc默認就使用了這種方式。
比如要重啓進程,得先確認進程被關閉後,再啓動進程。

如果使用nohupjava &這兩種方式,也是沒法直接做到這一點的。
這時大家結束進程的方式有幾種:發送SIGTERM(kill -15)信號給進程,或是在代碼中預埋觸發System.exit(0)的邏輯。
但這些操作都是類似於異步操作,因此只能在腳本中循環檢查進程狀態,來判斷進程是否結束。

4. 捕獲自定義信號。

有些人使用捕獲信號(比如-15、-12)的方式來做一些特定操作。
jsvc默認屏蔽了-12的信號,給它發送-12的信號是沒有任何作用的。
如果給一個普通的java進程發送-12信號,如果它沒有捕獲這個信號的邏輯,這個進程是會crash的。

jsvc支持捕獲-12的信號

5. 進程在crash後會自動重新啓動。

比如不小心使用kill -9終止java進程後,守護進程會自動重啓java進程。
這個是之前同事告訴我的。

會輸出如下信息:
Service killed by signal 9
Waiting 60 s to prevent looping

之前遇到過這種情況:
在內存不足時,java進程會啓動失敗,但是jsvc的後臺進程會不斷的去重啓java進程進行重試。

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