首頁 > 軟體

java啟動引數之謎的排查過程

2022-06-07 22:03:40

背景

最近遇到一個有意思的事情,java應用執行在阿里雲的ack叢集中,某一天有個應用啟動突然發現阿里雲上的agent都沒有註冊了,於是開始排查原因。

排查過程

我們的應用是java應用,jdk版本是Open-jdk8,阿里雲agent是直接注入到容器中的,因此會將agent啟動引數自動注入到 JAVA_TOOL_OPTIONS 環境變數中,當應用啟動時會自動帶上agent啟動引數。

agent沒註冊,首先檢查應用的啟動紀錄檔,發現應用是啟動成功的,tomcat埠都是正常的。仔細觀察紀錄檔,發現了問題。由於agent 啟動引數是注入到 JAVA_TOOL_OPTIONS 中的,通常jvm 在啟動的時候會優先載入 JAVA_TOOL_OPTIONS,紀錄檔中會出現 Picked up JAVA_TOOL_OPTIONS 的字樣,如下圖所示,但是問題現場卻沒有這一行和agent相關的啟動紀錄檔,說明 jvm 啟動的時候並沒有載入 JAVA_TOOL_OPTIONS。

我們開始懷疑是 agent啟動引數 的問題,以為是agent在容器重建時沒有將啟動引數注入到環境變數中。但是通過環境變數一看,發現 JAVA_TOOL_OPTIONS 是在的,而且每個agent的引數都是齊全的。

這個時候就開始懷疑是不是啟動指令碼的問題,是不是有人在啟動指令碼中加了unset JAVA_TOOL_OPTIONS,因為當存在JAVA_TOOL_OPTIONS時,使用jdk相關的命令都會帶上JAVA_TOOL_OPTIONS中的引數,造成一定的困擾,所以有時候在排查問題的時候會先unset掉這個變數,但是檢查完指令碼也沒有問題。

最後開始諮詢阿里雲的工程師,懷疑是不是agent或者容器環境有問題。經過反覆比較正常容器和問題容器的JAVA_TOOL_OPTIONS啟動引數,發現問題容器因為多載入一個agent,JAVA_TOOL_OPTIONS多出來一段引數,去掉這段引數就能恢復正常,加上就會有問題。到這裡,可能正常的思路都是懷疑是多出來的引數造成的。但在排查其他正常容器時發現,有的容器即使有這一段引數也能正常啟動。

這個時候,阿里雲的工程師懷疑是不是引數太長導致的,因為有問題的容器的應用名字比較長,於是我們開始測試,發現確實是這個問題,如下圖所示。隨後確定了問題所在,jdk8 在載入預設環境變數時會檢查長度,當大於1024位元組時就會載入失敗

環境變數

在jdk相關的環境變數中,有兩種預設的環境變數 JAVA_TOOL_OPTIONS_JAVA_OPTIONS

JAVA_TOOL_OPTIONS:在jdk8及之前版本中,該變數是最標準的,所有虛擬機器器都能識別和應用的環境變數,在jdk9之後被JDK_JAVA_OPTIONS所取代。該變數限制1024位元組,在不同虛擬機器器中表現不一樣,有的是載入失敗,有的是擷取一段。

_JAVA_OPTIONS:也是預設的環境變數,但是它是JVM廠家自定義的,可以覆蓋JAVA_TOOL_OPTIONS,但各廠家的命名不同,_JAVA_OPTIONS是Oracle的JVM,而IBM的則是用IBM_JAVA_OPTIONS。

因此為避免出現問題,我們應該儘量避免使用預設的環境變數,通常情況下可以在指令碼中自定義啟動變數如 JAVA_OPTSSPRINGBOOT_OPTS等等。然後在啟動java時顯式的指定啟動引數。

java [-options] -jar xxx.jar [args…]
可以寫成
JAVA_OPTS="[-options]"
JAVA_ARGS="[args…]"
java ${JAVA_OPTS} -jar xxx.jar ${JAVA_ARGS}

附:啟停指令碼

專案打包後在測試環境的啟停都是個體力活,剛好又給筆者遇到了,綜合別人的指令碼記錄了一下

判斷 Java 程序是否存在

APP_NAME=xxx.jar
pid=jps -l | grep $APP_NAME

if [ -z $pid ]; then
 	echo "$APP_NAME started"
 else
 	echo "$APP_NAME stoped"
 fi
 
# 普通程序的
# pid=ps -ef | grep $APP_NAME | grep -v grep | awk '{print $2}'

啟停指令碼

APP_NAME=xxxx-1.0-SNAPSHOT.jar

pid=0
checkpid() {
    javaps=`jps -l | grep $APP_NAME`
    if [ -n "$javaps" ]; then
    	pid=`echo $javaps | awk '{print $1}'`
    else
    	pid=0
}

start() {
    checkpid
    if [ $psid -ne 0 ]; then
    	echo "$APP_NAME already started"
    else
    	echo "Starting $APP_NAME ..."
    	`nohup java -jar $APP_NAME > $APP_NAME'.out' 2>&1 &`
    	checkpid
    	if [ $pid -ne 0]; then
    		echo "$APP_NAME start success"
    	else
    		echo "$APP_NAME start faild"
    	fi
    fi
}

stop() {
    checkpid
    if [ $pid -ne 0 ]; then
    	echo "Stoping $APP_NAME..."
        kill -9 $pid
        if [$? -eq 0 ]; then
        	echo "$APP_NAME stop success"
        else
        	echo "$APP_NAME stop faild"
       	fi
    else
    	echo "$APP_NAME already stoped"
   	fi
}


case "$1" in
 'start')
 start
 ;;
 'stop')
 stop
 ;;
 'restart')
 stop
 start
 ;;
 *)	# 其他任何情況
 
echo "help: $0 {start|stop|restart}"
echo "例子: ./deploy start
exit 1
esac
exit 0

總結

到此這篇關於java啟動引數之謎的文章就介紹到這了,更多相關java啟動引數內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com