javaweb問題集錦: java服務使用linux命令重啟自身服務
一.前言
最近server服務隔個兩天就出現"卡死"的現象, 必須要手動重啟服務, 才能正常使用; 後來各種查日誌, 查百度,可以復現問題, 主要原因是Hikari資料庫連線池中的連線用完了, 其他Client 訪問Server中的API時候, 一直在等待Hikari連線, 出現了超時的現象;
只能通過暴力重啟server服務,釋放連線,API接口才能正常訪問; 百度了一圈,解決這種問題方案總結如下:
1.增加伺服器硬體配置, 從而增大Hikari pool size , 優化Hikari引數配置, 參考
2.Server API介面優化, 優化sql語句,限制sql語句查詢條件,比如time, limit,等; 限制介面頻繁呼叫等等;
3.Server服務負載均衡,併發優化等(好高階,目前還沒有涉及到)
4.伺服器自己檢測自己API介面,出現超時,自動重啟伺服器
接下來這篇文章,就講講JAVA服務怎麼使用linux命令重啟自身服務,中間遇到了多少坑,流下了多少猿淚!
二.linux執行重啟java服務指令碼
由於對linux指令碼不是很熟悉, 在網上加了一個qq群, 感謝群裡的"小科"大神, 幫我寫了一個重啟指令碼, 還幫我分析了一下午問題,最後按照"小科"大神提供的線索"程序"問題, 順藤摸瓜, 終於解決問題! 在此感謝素昧相識的"小科"大神, 好人一生平安!
將此指令碼檔案放在伺服器資料夾中, 最好放在你程式的目錄中, 這樣不會出現環境,路徑的問題;
restart.sh
#!/bin/bash #Time: 2018-12-21 13:36:19 PID=`netstat -nlpt|grep -w "8082"|awk '{print $7}'|grep -oE "[0-9]+"` if [ -z ${PID} ];then echo "程序不存在!開始重新啟動。。" nohup java -Dlog4j.configurationFile=log4j2.xml -Dvertx.disableDnsResolver=true -Dio.vertx.ext.auth.prng.algorithm=NativePRNGNonBlocking -jar mx-appserver-1.1-fat.jar -conf config.json & 2>&1 else echo "開始結束${PID}程序,重新啟動。。。" kill -9 ${PID} nohup java -Dlog4j.configurationFile=log4j2.xml -Dvertx.disableDnsResolver=true -Dio.vertx.ext.auth.prng.algorithm=NativePRNGNonBlocking -jar mx-appserver-1.1-fat.jar -conf config.json & 2>&1 fi
三.Java程式中執行linux命令,呼叫restart.sh指令碼,重啟自身
方法一:可以正常執行linux restart.sh指令碼檔案 ,但是日誌檔案不能持續輸出和儲存
主要運用 Runtime.getRuntime().exec(command) 執行linux命令
private void executeCmd() {
try {
String cmd = "restart.sh";
//String[] command = {"/bin/sh", "-c", cmd};
String[] command = {"/bin/sh", cmd};
//String[] command = {"/bin/nohup","sh", cmd, "&"};
System.out.println("sout伺服器重啟response data:" +Arrays.toString(command));
_LOG.info("伺服器重啟request data:{}" , Arrays.toString(command));
Process ps = Runtime.getRuntime().exec(command);
BufferedReader br = new BufferedReader(new InputStreamReader(ps.getInputStream()));
StringBuffer sb = new StringBuffer();
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
}
String result = sb.toString();
System.out.println("sout伺服器重啟response data:" + result);
_LOG.error("伺服器重啟response data:" + result);
} catch (IOException e) {
_LOG.error("execute(Tuple, BasicOutputCollector)", e);
e.printStackTrace();
}
}
方法二: 可以正常執行linux restart.sh指令碼檔案 ,但是日誌檔案不能持續輸出和儲存
主要運用 ProcessBuilder pb = new ProcessBuilder("nohup","sh","restart.sh","&");執行linux命令
private void executeCmd2() {
try {
String cmd = "restart.sh";
//String[] command = {"/bin/sh", "-c", cmd};
String[] command = {"/bin/sh", cmd};
//String[] command = {"/bin/nohup","sh", cmd, "&"};
System.out.println("sout伺服器重啟response data:" +Arrays.toString(command));
_LOG.info("伺服器重啟request data:{}" , Arrays.toString(command));
ProcessBuilder pb = new ProcessBuilder("nohup","sh","restart.sh","&");
pb.start();
} catch (IOException e) {
_LOG.error("execute(Tuple, BasicOutputCollector)", e);
e.printStackTrace();
}
}
方法三: 可以正常執行linux restart.sh指令碼檔案 ,日誌檔案nohup.out可以正常輸出和儲存
主要涉及到linux程式程序問題 File是你的日誌檔案
private void executeCmd3() {
// 不使用Runtime.getRuntime().exec(command)的方式,因為無法設定以下特性
// Java執行本地命令是啟用一個子程序處理,預設情況下子程序與父程序I/O通過管道相連(預設ProcessBuilder.Redirect.PIPE)
// 當服務執行自身重啟的命令時,父程序關閉導致管道連線中斷,將導致子程序也崩潰,從而無法完成後續的啟動
// 解決方式,(1)設定子程序IO輸出重定向到指定檔案;(2)設定屬性子程序的I/O源或目標將與當前程序的相同,兩者相互獨立
try {
File file = null;
ProcessBuilder pb = new ProcessBuilder("sh","restart.sh");
if (file == null || !file.exists()) {
// 設定屬性子程序的I/O源或目標將與當前程序的相同,兩者相互獨立
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
pb.redirectInput(ProcessBuilder.Redirect.INHERIT);
} else {
// 設定子程序IO輸出重定向到指定檔案
// 錯誤輸出與標準輸出,輸出到一塊
pb.redirectErrorStream(true);
// 設定輸出日誌
pb.redirectOutput(ProcessBuilder.Redirect.appendTo(file));
}
// 執行命令程序
pb.start();
} catch (IOException e) {
e.printStackTrace();
}
}
最後採取的是方法三,可以正常重啟自己,並且日誌正常寫入和輸出!
每個專案的環境及需求不一樣,所以此篇文章只是記錄我的環境下的解決方法, 大家可以參考, 萬變不離其宗!