守护进程

实现基本的进程守护功能

package main

import (
	"context"
	"fmt"
	"github.com/shirou/gopsutil/v3/process"
	"os"
	"os/exec"
	"os/signal"
	"syscall"
	"time"
)

var CMD *exec.Cmd

type Bye struct {
	Cmd   *exec.Cmd
	Error error
}

func _clean(ctx context.Context, name string) error {
	// kill process by name
	processes, err := process.ProcessesWithContext(ctx)
	if err != nil {
		return err
	}
	for _, p := range processes {
		_name, err := p.NameWithContext(ctx)
		if err != nil {
			return err
		}
		if _name == name {
			return p.KillWithContext(ctx)
		}
	}
	return nil
}

func _fork(env []string) *exec.Cmd {
	// args := []string{ "atg01", "arg02", "arg03" }
	// cmd := exec.Command("./lib/app", args...)
	cmd := exec.Command("./bin/app")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Env = env
	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
	return cmd
}

func _exit() error {
	pid := CMD.Process.Pid
	fmt.Printf("[App pid=%d]child process(%s) existing\n", pid, CMD.String())
	CMD.Process.Release()
	err := syscall.Kill(-pid, syscall.SIGKILL)
	if err != nil {
		return err
	}
	return nil
}

func main() {
	/*
			daemon
		  		|______app

			需要实现由daemon来管理app进程的存活
			1. 当daemon进程退出,则app也一并退出
			2. 当app进程退出,daemon需要把app进程重新启动
	*/
	selfPid := os.Getpid()
	//清理残留的app进程,因为go监听不到SIGKILL(kill -9)
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	err := _clean(ctx, "appName")
	if err != nil {
		fmt.Printf("failed to clean old app process: %s\n", err.Error())
		return
	}

	os.Setenv("ENV_KEY", "ENV_Value") // set env if you need
	env := os.Environ()
	CMD = _fork(env)
	err = CMD.Start()
	if err != nil {
		fmt.Printf("[Daemon pid=%d]failed to start app process: %s\n", selfPid, err)
		return
	} else {
		fmt.Printf("[Daemon pid=%d]succeed start app process, app pid=%d\n", selfPid, CMD.Process.Pid)
	}
	defer _exit()

	//监听信号
	signals := make(chan os.Signal)
	signal.Notify(signals, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL, syscall.SIGQUIT)

	//监听子进程
	done := make(chan Bye, 1)
	go func() {
		err := CMD.Wait()
		item := Bye{
			Cmd:   CMD,
			Error: err,
		}
		done <- item
	}()

	for {
		select {
		case <-time.After(time.Second * 5):
			fmt.Printf("[%s]heart beat...\n", time.Now().Format("2006-01-02 15:04:05"))
		case s := <-signals:
			fmt.Printf("Daemon got siganl=%d, now[%s] to exit...\n", s, time.Now().Format("2006-01-02 15:04:05"))
			goto ForEnd
		case bye := <-done:
			if bye.Error != nil {
				fmt.Printf("[App pid=%d]process(%s) finished with error = %s\n", bye.Cmd.Process.Pid, bye.Cmd.String(), bye.Error.Error())
			}
			fmt.Printf("\n%+v\n\n", bye.Cmd.ProcessState)
			bye.Cmd.Process.Release()
			time.Sleep(time.Second * 3)

			CMD = _fork(env)
			err = CMD.Start()
			if err != nil {
				fmt.Printf("[Daemon pid=%d]failed to start app process: %s\n", selfPid, err)
				return
			} else {
				fmt.Printf("[Daemon pid=%d]succeed start app process, pid=%d\n", selfPid, CMD.Process.Pid)
			}
			go func() {
				err := CMD.Wait()
				item := Bye{
					Cmd:   CMD,
					Error: err,
				}
				done <- item
			}()
		}
	}
ForEnd:
	fmt.Printf("[Daemon pid=%s]daemon process exiting...\n", selfPid)
}

  

 

posted @ 2021-09-09 11:48  lixin[at]hitwh  阅读(69)  评论(0编辑  收藏  举报