java啟動參數之謎的排查過程
背景
最近遇到一個有意思的事情,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_OPTS
、SPRINGBOOT_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啟動參數內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Spring Boot 啟動、停止、重啟、狀態腳本
- JMeter自定義日志與日志分析的實現
- Spring Boot jar 啟動時設置環境參數的操作
- Nginx設置成服務並開機自動啟動的配置
- Shell函數返回值方式