golang实现守护进程,包含功能:
守护进程只创建一次
平滑创建业务进程
业务进程挂起,守护进程能监听,并重启新启业务进程
守护进程退出,也能保证业务进程退出
业务进程≈子进程
不影响业务进程逻辑
以Linux平台为主,其他平台暂时没有实施条件
目前实现方式是golang+shell脚本形式,所以golang创建进程的方式放在下一篇进行阐述
编写gt-daemon.sh脚本,作为守护进程
#!/bin/bash# dos2unix gt-daemon.sh
# sh gt-daemon.sh -t start -c param1 -d param2 -e param3BASE_PATH=$(cd $(dirname $0)pwd
)LOG_FILE=$BASE_PATH/gt-daemon.logPID_FILE=$BASE_APTH/gt-daemon.pidUSAGE="Usage: gt-daemon.sh [start|stop|status|help]"# project-name=go_startisE=false
count=0
scount=0function startSubProcess() {while true; dots=$(date "+%Y-%m-%d-%H-%M-%S")procnum=$(ps -ef | grep "go_start" | grep java | grep -v grep | wc -l)if [ $procnum -eq 0 ]; thennohup $1/go_start -c $1/$PARAM1 -d $PARAM2 -e $PARAM3 >/dev/null 2>&1 &isE=truescount++echo $ts: start! flag=$isE basepath=$1 param1=$PARAM1 param2=$PARAM2 param3=$PARAM3 >>$LOG_FILEfiif [[ $scount -ge 3 ]]; thenbreakfisleep 5done
}function stopSubProcess() {while [ true ]; doif [ $isE ]; thenproc=$(ps -ef | grep "go_start" | grep go_start | grep -v grep | wc -l)ts2=$(date "+%Y-%m-%d-%H-%M-%S")if [[ $proc -ne 0 ]]; thentmpproc=$(ps -ef | grep "go_start" | grep -v grep | awk '{print $2}')echo "Stop subProcess, pid is ${tmpproc}"kill -9 $tmpprocecho $ts2: kill subProcess >>$LOG_FILEisE=falsebreakfielseif [ $count -ge 3 ]; thenecho $ts2: subProcess is killed! >>$LOG_FILEbreakficount++sleep 1fidone
}function stopPrarentProcess() {if [ ! -f ${PID_FILE} ]; thenecho "gt-daemon.sh is not running!"elsepid=$(cat ${PID_FILE})procnum=$(ps -ef | grep "gt-daemon.sh" | grep -v grep | wc -l)if [ $procnum -ge 1 ]; thenecho "Stop process, pid is ${pid}"kill -9 $pidif [[ $? -eq 0 ]]; thenecho "Remove pid file"rm -rf ${PID_FILE}elseecho "Stop process is failed!"fielseecho "gt-daemon.sh process is not found, remove pid file"rm -rf ${PID_FILE}fifi
}function stop() {if [ ! -f ${PID_FILE} ]; thenecho "gt-daemon.sh is not running"elsestopSubProcessstopPrarentProcessfi
}function status() {if [ ! -f ${PID_FILE} ]; thenecho "gt-daemon.sh is not running!"elsepid=$(cat ${PID_FILE})echo "gt-daemon.sh is running, pid is ${pid}"fi
}case $1 in
"start")if [ $# -lt 2 ]; thenecho "参数小于8个,退出!"exit 1fiif [ -f ${PID_FILE} ]; thenpid=$(cat ${PID_FILE})procnum=$(ps -ef | grep "gt-daemon.sh" | grep -v grep | wc -l)if [ $procnum -ge 3 ]; thenecho "gt-daemon.sh is already running, pid is ${pid}"exit 1fifiPARAM1=$4PARAM2=$6PARAM3=$8startSubProcess $BASE_APTH &pid=$!echo $pid >$PID_FILEecho "Start success, pid is ${pid}";;
"stop")stop;;
"status")status;;
"help")echo ${USAGE};;
*)echo ${USAGE};;
esac
main()方法中启动守护进程
func main() {// 全局变量设置...// 判断是否启动守护进程basePath, _ := os.Getwd()baseDir := filepath.Dir(basePath)fmt.Println(fmt.Sprintf("basePath is %s and baseDir is %s", basePath, baseDir))// 获取go_start二进制文件所在路径,// gt-daemon.sh与go_start二进制文件同级,// 判断该路径下是否有gt-daemon.sh// 如果有,则启动(由业务进程启动),然后由守护进程监听业务进程// 如果没有,则不启动守护进程,直接启动业务进程gwpath := basePath + "gt-daemon.sh"fmt.Println(fmt.Sprintf("gt-daemon.sh path is %s", gwpath))exists, _ := PathExists(gwpath)fmt.Println(fmt.Sprintf("文件路径存在: %v\n", exists))if exists {res := execShell("ps -ef | grep 'gt-daemon.sh' | grep -v grep | wc -l")procnum, err2 := strconv.Atoi(res)if err2 != nil {fmt.Println(fmt.Sprintf("gt-daemon.sh result exchange err: %s", err2.Error()))} else {fmt.Println(fmt.Sprintf("gt-daemon.sh process num is %d\n", procnum))if procnum == 0 {cmd := fmt.Sprintf("sh %s -t start -c %s -chost %s -cpsw %s >/dev/null 2>&1 &", gwpath, argv.config, argv.chHost, argv.chPassword)fmt.Println("command to execute : " + cmd)execShell(cmd)}}}// 其他业务进程相关代码...
}
相关工具方法
func PathExists(path string) (bool, error) {_, err := os.Stat(path)if err == nil {return true, nil}if os.IsNotExist(err) {return false, nil}return false, err
}func execShell(command string) string {cmd := exec.Command("/bin/bash", "-c", command)bytes, err := cmd.Output()if err != nil {fmt.Println(fmt.Sprintf("execute [%s] error, %s", command, err.Error()))return ""}resp := strings.Replace(string(bytes), "\n", "", -1)return resp
}
gt-daemon.sh
go_start
两个文件在同一个目录下,比如linux环境的/tmp/
启动时:sh gt-daemon.sh -t start -c param1 -d param2 -e param3
首先查看守护进程和业务进程是否启动,查看方式
ps -ef | grep gt-daemon
ps -ef | grep go_start
杀掉子进程,查看是否新起子进程
kill 子进程PID
ps -ef | grep go_start -- 不存在go_start进程,说明kill成功
ps -ef | grep go_start -- 一会再执行,发现存在go_start进程,说明新起go_start进程成功
优点:
实现简单,守护进程独立代码之外
缺点:
需要单独维护守护进程脚本
综上:考虑纯代码形式再实现一版守护进程