linux環境中Java服務通過shell腳本重啟(升級)自己


 

今天遇到一個遠程升級的需求,通過接口去觸發終端服務的接口,重新拉取最新的jar包,並重啟終端服務,這個終端服務是用java寫的。 實現該需求,兩個步驟,一個是需要一個shell腳本:拉取jar包、kill掉服務、啟動服務;還有一個就是java中收到消息去調用shell腳本。

腳本

啟動命令:

/root/dtest/upgrade.sh jar-name 端口 jar下載地址 jar存放路徑

 

 1 # !/bin/bash
 2 echo "start upgrade......"
 3 ## 判斷參數是否正確
 4 ########### jar包名稱 ############
 5 APPLICATION_NAME=""
 6 if [ ! $1 ]; then
 7   echo "待執行的jar名稱 IS NULL"
 8   exit 1
 9 else
10     APPLICATION_NAME=$1".jar"
11 fi
12 SERVER_PORT=""
13 if [ ! $2 ]; then
14   echo "端口 IS NULL"
15   exit 1
16 else
17     SERVER_PORT=$2
18 fi
19 ########### 軟件包下載地址 ############
20 FILE_URL=""
21 if [ ! $3 ]; then
22   echo "軟件包下載地址 IS NULL"
23   exit 1
24 else
25     FILE_URL=$3
26 fi
27 BASE_PATH=""
28 if [ ! $4 ]; then
29   echo "軟件包下載地址 IS NULL"
30   BASE_PATH="/usr/local/docker"
31 else
32   BASE_PATH=$4
33 fi
34 ## kill 掉進程
35 PROCESS=`ps -ef|grep $APPLICATION_NAME|grep -v grep|grep -v PPID|awk '{ print $2}'`
36 for i in $PROCESS
37 do
38   echo "停止服務:kill the $APPLICATION_NAME process [ $i ]"
39   kill -9 $i
40 done
41 ##備份
42 rm -rf $APPLICATION_NAME
43 ## 下載應用服務包
44 echo "download the application package"
45 echo "升級包下載命令 wget $FILE_URL -O "/root/dtest/"$APPLICATION_NAME"
46 wget $FILE_URL -O "/root/dtest/"$APPLICATION_NAME
47 echo "升級包下載完成!!!"
48 ## 啟動服務
49 echo "開始啟動 $1 服務"
50 chmod 777 $APPLICATION_NAME
51 nohup java -server -jar -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m -Xms256m -Xmx512m -Xmn256m -Xss256k -XX:SurvivorRatio=8 -XX:+UseConcMarkSweepGC $BASE_PATH"/"$APPLICATION_NAME --server.port=$SERVER_PORT   2>&1 &
52 echo "服務啟動命令:java -server -jar -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m -Xms256m -Xmx512m -Xmn256m -Xss256k -XX:SurvivorRatio=8 -XX:+UseConcMarkSweepGC $BASE_PATH"/"$APPLICATION_NAME --server.port=$SERVER_PORT > /dev/null 2>&1 &"
53 for st in $(seq 1 20)
54     do
55 #        PID=$(netstat -nlp | grep :$EXECUTE_PORT | awk '{print $7}' | awk -F"/" '{ print $1 }');
56         PID=$(netstat -nlp | grep :$SERVER_PORT | awk '{print $7}' |sed 's/\([0-9]*\).*/\1/g');
57         if [ $st -eq 20 ] && [ -z "$PID" ]; then
58             echo "服務啟動失敗" ### $PID 為空
59             break
60         fi
61 
62         if [ -z "$PID" ]; then
63             sleep 2
64             echo $st"服務啟動中...." ### $PID 為空
65         else
66             echo "服務名稱:$APPLICATION_NAME ,端口號為:$SERVER_PORT ,進程號為:$PID 啟動成功 , 耗時:$[$[st-1]*3] seconds!!!"
67             break
68         fi
69     done

 

Java程序

java調用shell腳本有多種方式,簡單粗暴的方式是:Runtime.getRuntime().exec() 

但現實給我上了一課,當kill掉自己服務后,后面的腳本也停止執行了,原因處在,當服務執行自身重啟的命令時,父進程關閉導致管道連接中斷,將導致子進程也崩潰,從而無法完成后續的啟動。

解決方式,

  1. 設置子進程IO輸出重定向到指定文件
  2. 設置屬性子進程的I/O源或目標將與當前進程的相同,兩者相互獨立

上代碼:(源碼下載

 1 package com.dzh.boot.demo.controller;
 2 
 3 import org.springframework.beans.factory.annotation.Value;
 4 import org.springframework.web.bind.annotation.RequestMapping;
 5 import org.springframework.web.bind.annotation.RestController;
 6 
 7 import java.io.File;
 8 import java.io.IOException;
 9 
10 /**
11  * 升級當前服務 --- 拉取最新jar,殺掉當前服務,啟動最新的jar
12  * @date 2021.4.9
13  */
14 @RestController
15 public class UpgradeController {
16 
17     //腳本的地址
18     @Value("${my.test.scriptPath}")
19     private String scriptPath;
20 
21     /**
22      * jar包的名稱  去掉.jar
23      */
24     @Value("${my.test.name}")
25     private String applicationName;
26 
27     /**
28      * 服務啟動的端口
29      */
30     @Value("${server.port}")
31     private String port;
32 
33     /**
34      * 最新jar包的下載地址
35      */
36     @Value("${my.test.fileUrl}")
37     private String fileUrl;
38 
39     /**
40      * jar包放置的路徑
41      */
42     @Value("${my.test.basePath}")
43     private String basePath;
44 
45     /**
46      * 觸發升級
47      * @return
48      * @throws Exception
49      */
50     @RequestMapping("run")
51     private String run() throws Exception {
52         ProcessBuilder sh = new ProcessBuilder("sh", scriptPath, applicationName, port, fileUrl, basePath);
53         asynExeLocalComand(null, sh);
54         return "成功";
55     }
56 
57     /**
58      * 用來檢查服務是否正常
59      * @return
60      * @throws IOException
61      */
62     @RequestMapping("getParam")
63     private String getParam() throws IOException {
64         return scriptPath + "  " + applicationName + "  " + fileUrl + "   " + basePath + "  " + port;
65     }
66 
67 
68 
69     public static void asynExeLocalComand(File file, ProcessBuilder pb) throws IOException {
70         // 不使用Runtime.getRuntime().exec(command)的方式,因為無法設置以下特性
71         // Java執行本地命令是啟用一個子進程處理,默認情況下子進程與父進程I/O通過管道相連(默認ProcessBuilder.Redirect.PIPE)
72         // 當服務執行自身重啟的命令時,父進程關閉導致管道連接中斷,將導致子進程也崩潰,從而無法完成后續的啟動
73         // 解決方式,(1)設置子進程IO輸出重定向到指定文件;(2)設置屬性子進程的I/O源或目標將與當前進程的相同,兩者相互獨立
74         if (file == null || !file.exists()) {
75             // 設置屬性子進程的I/O源或目標將與當前進程的相同,兩者相互獨立
76             pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
77             pb.redirectError(ProcessBuilder.Redirect.INHERIT);
78             pb.redirectInput(ProcessBuilder.Redirect.INHERIT);
79         } else {
80             // 設置子進程IO輸出重定向到指定文件
81             // 錯誤輸出與標准輸出,輸出到一塊
82             pb.redirectErrorStream(true);
83             // 設置輸出日志
84             pb.redirectOutput(ProcessBuilder.Redirect.appendTo(file));
85         }
86         // 執行命令進程
87         pb.start();
88     }
89 
90 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM