1. 程式人生 > >javaweb問題集錦: java服務使用linux命令重啟自身服務

javaweb問題集錦: java服務使用linux命令重啟自身服務

一.前言

      最近server服務隔個兩天就出現"卡死"的現象,   必須要手動重啟服務,   才能正常使用;  後來各種查日誌, 查百度,可以復現問題, 主要原因是Hikari資料庫連線池中的連線用完了,   其他Client 訪問Server中的API時候, 一直在等待Hikari連線, 出現了超時的現象;

只能通過暴力重啟server服務,釋放連線,API接口才能正常訪問;  百度了一圈,解決這種問題方案總結如下:

    1.增加伺服器硬體配置,  從而增大Hikari pool size , 優化Hikari引數配置, 參考

HikariCP 連線池配多大合適

    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指令碼,重啟自身

     參考連結在java程式中開啟另一個java程式

     參考連結Java執行shell遇到的各種問題

     方法一:可以正常執行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();
        }
    }

最後採取的是方法三,可以正常重啟自己,並且日誌正常寫入和輸出!

每個專案的環境及需求不一樣,所以此篇文章只是記錄我的環境下的解決方法,  大家可以參考,  萬變不離其宗!