实现基本的进程守护功能
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) }