標籤:
通常對於服務來說,過長的命令往往讓人感到厭煩,人們需要的只是簡單的操作,並且能夠支援複雜的功能,對於Java開發的服務來說更是如此。
一個比較複雜的Jar服務使用Java啟動,命令如下
java -Xms512m -Xmx512m -jar fuck.jar –config config.server -port 10086
實際上許多虛擬機器的語言的Host命令格式也是類似的。
我們分析可以知道對於基於虛擬機器的語言,命令列基本上是 host+vm運行參數+執行檔案路徑+輸入參數。
當然如果參數較少,我們完全不用寫一個Launcher指令碼來管理服務。
Launcher指令碼需要提供的命令至少有:
1. start
2. stop
3. restart
4. status
5. help
實現
在Linux系統上,啟動指令碼應該是簡單的,不許要過多依賴的,一般而言推薦使用shell指令碼,實際上很多軟體在Linux上的Launcher都是使用Shell語言。android studio,brackets Codebox ,甚至Chrome Firefox都有shell指令碼的啟動器。
在Windows上早期,部分軟體使用cmd來寫啟動器,然而cmd的功能孱弱,微軟適時的推出了PowerShell,PowerShell在功能上非常強大,甚至要優於Shell,所以採用PowerShell來寫啟動器,並且寫一個cmd輔助指令碼啟動PowerShell。
@echo offif not exist "%~dp0launcher.ps1" goto NotFoundPowerShell -NoProfile -NoLogo -ExecutionPolicy unrestricted -Command "[System.Threading.Thread]::CurrentThread.CurrentCulture = ‘‘; [System.Threading.Thread]::CurrentThread.CurrentUICulture = ‘‘;& ‘%~dp0launcher.ps1‘ %*"goto :EOF:NotFoundecho Not Found launcher.ps1 in %~dp0,Please reset your launcherPAUSE
設定檔的讀取
選擇合適的設定檔能夠簡化操作,對於簡單的Shell指令碼而言,複雜的設定檔是難以實現的。我將Launcher的設定檔分為兩類,一個是JVM的參數,也就是上面的 “-Xms512m -Xmx512m” 這種檔案的格式就是按行讀取,行首存在‘#’字元就拋棄。
另一類是基於INI-Style 的設定檔,主要是JDK的路徑,需要啟動並執行jar包的路徑,以及重新導向的設定,由於Windows和Linux的檔案系統存在差異,所以在涉及到檔案系統的設定遷移到了Windows和Posix節
Bash解析Ini檔案:
function GetPrivateProfileString(){if [ ! -f $1 ] || [ $# -ne 3 ];thenreturn 1fiblockname=$2fieldname=$3begin_block=0end_block=0cat $1 | while read linedoif [ "X$line" = "X[$blockname]" ];thenbegin_block=1continuefiif [ $begin_block -eq 1 ];thenend_block=$(echo $line | awk ‘BEGIN{ret=0} /^\[.*\]$/{ret=1} END{print ret}‘)if [ $end_block -eq 1 ];then#echo "end block"breakfineed_ignore=$(echo $line | awk ‘BEGIN{ret=0} /^#/{ret=1} /^$/{ret=1} END{print ret}‘)if [ $need_ignore -eq 1 ];then#echo "ignored line:" $linecontinuefifield=$(echo $line | awk -F= ‘{gsub(" |\t","",$1); print $1}‘)#####Fix Me We Support Space Valuevalue=$(echo $line | awk -F= ‘{gsub("","",$2); print $2}‘)#echo "‘$field‘:‘$value‘"if [ "X$fieldname" = "X$field" ];then#echo "result value:‘$result‘"echo $valuebreakfifidonereturn 0}
PowerShell解析Ini檔案:
Function Parser-IniFile{ param( [Parameter(Position=0,Mandatory=$True,HelpMessage="Enter Your Ini File Path")] [ValidateNotNullorEmpty()] [String]$File ) $ini = @{} $section = "NO_SECTION" $ini[$section] = @{} switch -regex -file $File { "^\[(.+)\]$" { $section = $matches[1].Trim() $ini[$section] = @{} } "^\s*([^#].+?)\s*=\s*(.*)" { $name,$value = $matches[1..2] # skip comments that start with semicolon: if (!($name.StartsWith(";"))) { $ini[$section][$name] = $value.Trim() } } } $ini}
JDK的檢測
查看Java路徑,通常來說,launcher指令碼會從launcher.cfg讀取Posix(Windows) 節的JAVA_HOME索引值,如果沒有JAVA_HOME的變數就讀取環境變數的JAVA_HOME,如果存在JAVA_HOME,但實際路徑上並不存在,或者沒有存在JAVA_HOME,那麼再從尋找java的路徑。而JAVA_HOME的設定可以在有多個JDK的時候仍然正確的選擇JDK.而不用帶來衝突。
jdkenv=$(GetPrivateProfileString launcher.cfg Posix JAVA_HOME)javabin=`which java`if [ -f "$jdkenv/bin/java" ]; thenjavabin="$jdkenv/bin/java"fi
Get-JavaSE函數是為了支援從註冊表查詢JDK安裝。所以單獨的寫了一個函數。
Function Get-JavaSE{ $jdk=$env:JAVA_HOME #This is regedit search java return $jdk}$JdkRawEnv=$IniAttr["Windows"]["JAVA_HOME"]$JavaEnv="$env:JAVA_HOME"IF($JdkRawEnv -eq $null){ $JavaEnv=Get-JavaSE}else{ $JavaEnv=$JdkRawEnv}
進程的管理
先說PowerShell,PowerShell是物件導向的,我們可以輕鬆的獲得進城對象。
我們使用Start-Process 啟動一個進程。在這個cmdlet中 我們添加 -PassThru就可以得到一個進程對象
$ProcessObj= Start-Process -FilePath "${JavaExe}" -PassThru -Argumentlist "${VMOptions} -jar ${PrefixDir}\${AppPackage} $Parameters" -RedirectStandardOutput "${StdoutFile}" -RedirectStandardError "${StdErrorFile}" -WindowStyle Hidden
$ProcessObj 可以拿到進程的Id,進程的鏡像名,以及進程的資源佔用情況。
當然進程對象在Get-Process也是可用的。
使用Get-Process獲得一個進程,如果有進程id再好不過。如果不存在id對應的進程則會拋出異常
$Obj=Get-Process -Id $javaid
結束一個進程需要Stop-Process 只需要輸入 -Id id即可。
而對於Linux,有檔案系統/proc,同樣可以實現PowerShell的功能。判斷進程是否存在可以檢測 /proc/id是否存在,可以檢查/proc/id/status 查看資源佔用什麼的。
我們在launcher指令碼所在目錄(通常也是jar包所在目錄)當啟動java進程成功後,我們將pid寫入到launcher.lock.pid 在需要停止和重啟的時候使用launcher.lock.pid儲存的id來操作即可。
在PowerShell中可以用Get-Content 讀取pid。在Linux中可以用cat。
最終
上述代碼已經託管到[email protected]
項目:ServiceLauncher 使用MIT協議,歡迎Pull Request.
多個平台的Java Launcher指令碼