身為一名coder赴穗,世界上最幸福的事莫過于開發(fā)別人沒開發(fā)出的功能炕吸;最近接到了一個需求,需要通過web頁面下達重啟命令利耍,后臺程序讀取到這個命令能夠實現(xiàn)自我重啟蚌本;
當時我們公司已經有其他項目實現(xiàn)了這個功能,是不過是通過另一個類似看門狗的程序來控制主程序的啟停操作隘梨,不過程癌。。轴猎。項目經理說我們最好能夠在主程序內部實現(xiàn)嵌莉,現(xiàn)場要是再部署另一個程序會很麻煩,呃(⊙﹏⊙)捻脖,好吧锐峭,頭腦風暴了一會兒之后心想這還不簡單?大概分為如下幾步:
1可婶、數據庫建一個任務表用于記錄啟停操作
2沿癞、寫一個定時任務用于讀取該任務表的標記位,每20秒執(zhí)行一次
3矛渴、web頁面點擊“重啟”操作之后修改任務標記位
4椎扬、定時任務監(jiān)控到標記位變化之后通過Java執(zhí)行重啟的shell腳本
列出了如上思路后開始實現(xiàn)。具温。蚕涤。。铣猩。
RuntimeUtils.java(用于執(zhí)行sh或bat腳本的工具類)
/**
* 執(zhí)行sh或bat腳本的工具類
*/
public class RuntimeUtils {
/**
*
* @Title: exec
* @Description: 簡化執(zhí)行命令行
* @param command 命令行
* @param envp 環(huán)境變量
* @param dir 路徑
* @return Process 返回類型
* @throws IOException
*/
public static Process exec(String command, String envp, String dir)
throws IOException {
String regex = "\\s+";
String args[] = null;
String envps[] = null;
if (command != null && command.length() != 0) {
args = command.split(regex);
}
if (envp != null && envp.length() != 0) {
envps = envp.split(regex);
}
return Runtime.getRuntime().exec(args, envps, new File(dir));
}
}
LinuxShell.java(新建一個類用于專門封裝一些linux操作)
/**
* Linux命令操作
*/
public class LinuxShell {
public static void restartCalc() throws Exception{
String command = "sh *** restart"; //***是程序名揖铜,sh后面就是要執(zhí)行的命令
String dir = VoltConfig.restartFilePath;
Process process = RuntimeUtils.exec(command, null, dir);
int i = process.waitFor();
System.exit(i);
}
}
web部分的代碼就不貼了。达皿。
寫完如上代碼之后天吓,后面在定時任務里調用,調用方式如下(由于部分代碼涉及到公司私密設計峦椰,就不貼出來了):
//判斷是否需要重啟
if(heartBeat.getRebootFlag().equals("0")) {
//執(zhí)行重啟腳本
if(VoltConfig.rebootType.equals("sh")) {
logger.info("監(jiān)測到重啟命令失仁,開始重啟...");
LinuxShell.restartCalc();
}
//TODO 待擴展,windows下執(zhí)行bat腳本
} else {
logger.info("未監(jiān)測到重啟命令");
}
開開心心的寫完后發(fā)現(xiàn)整個難度也就那樣们何,后面就是打包驗證了
打包到部署到啟動一切都很順利,下面就是結合web來實現(xiàn)指令控制了
web頁面下達重啟命令后控轿,我預計的效果并沒有達到冤竹,日志如下:
怎么只停止了拂封?為什么沒有啟動?
反復查看了那幾行代碼鹦蠕,盲猜程序執(zhí)行l(wèi)inux命令的方法是異步的冒签,腳本還沒執(zhí)行完程序就停止了
于是把System.exit(i);這一句刪掉,下面重啟打包測試钟病。萧恕。。肠阱。
還是不行票唆!還是跟之前一樣的效果,程序依然只是停止屹徘,沒有執(zhí)行啟動操作走趋,為什么?
非要我面向百度編程噪伊?
百度了一下發(fā)現(xiàn)別人也有這種需求簿煌,,鉴吹,好吧姨伟,是我自作多情了,看了一下基本就是程序自我重啟本身這條思路就不可行6估6峄摹!肆糕!都是通過另一個程序來控制主程序的啟停般堆!WTF?诚啃?淮摔?
不甘心的我反復思考了為什么,總結了以下可能的情況(百度找不到原因始赎,只能自我主觀地總結和橙,希望看到本篇文章的大佬指點一二):
程序從開始執(zhí)行l(wèi)inux命令,一直到linux命令執(zhí)行結束都是在JVM里執(zhí)行造垛,并不是理想中的程序只負責調用一下shell腳本魔招,剩下的交由shell腳本去自己完成執(zhí)行。
當JVM執(zhí)行重啟命令執(zhí)行到程序停止時五辽,此時JVM已經被銷毀办斑,接下來的步驟沒有程序去執(zhí)行了,所以腳本里剩下的啟動操作就沒執(zhí)行
總結上面原因后,大概這條路就行不通了乡翅,還是得想別的辦法
反觀當前狀況并不是一無所獲鳞疲,起碼我收到命令能夠自我停止( ̄??)
后面就需要一個監(jiān)控程序來監(jiān)控程序的運行狀態(tài),當程序處于停止狀態(tài)時蠕蚜,自動執(zhí)行啟動操作尚洽,可是說好不再寫輔助程序的。靶累。腺毫。
不寫Java,我們可以通過shell腳本來實現(xiàn)對程序運行狀態(tài)的監(jiān)控
順了一下監(jiān)控程序的實現(xiàn)思路大概就是:
1挣柬、查看當前程序的在運進程個數
2潮酒、如果進程數為0,說明程序處于停止狀態(tài)凛忿,此時執(zhí)行啟動命令
3澈灼、如果進程數大于0,說明程序在運狀態(tài)
4店溢、上面步驟需要定時循環(huán)
shell腳本如下:
#!/bin/bash
date=`date +%Y-%m-%d_%H:%M:%S`
##監(jiān)控腳本日志存放路徑
recorddir=/home/voltCalc/Test/logs
##監(jiān)控腳本日志文件名稱
recordfile=listener.`date +%Y-%m-%d`.log
##Test啟動文件所在路徑
Testurl=/home/Test
##normal--正常運行;upgrade--升級
usefor=normal
monitor()
{
if [ ! -d ${recorddir} ] ; then
mkdir -p ${recorddir}
fi
if [ "${usefor}" == "normal" ] ; then
##Test的進程數,由于腳本名稱為Test.sh叁熔,所以排除了對Test的搜索
count=`ps -ef|grep Test|grep -v "grep"|grep -v "Test.log"|wc -l`
if [ ${count} -gt 0 ] ; then
echo "${date},Test服務運行正常!"
else
sh ${Testurl}/run.sh
echo "${date},重啟了Test服務床牧!"
fi
elif [ "${usefor}" == "upgrade" ] ; then
echo "${date},系統(tǒng)升級荣回,不需要重新啟動Test服務!"
else
echo "${date},參數配置異常,請檢查usefor參數戈咳!"
fi
}
for (( i=1; i>0; i=1)); do
monitor >> ${recorddir}/${recordfile}
sleep 5
done
exit 0
于是乎心软,程序不用變,當頁面下達重啟命令后
程序停止著蛙,監(jiān)控腳本監(jiān)控到停止狀態(tài)執(zhí)行啟動操作
需求完成删铃!
就是升級的時候需要多一個shell腳本了,emm....