守护进程
实现基本的进程守护功能
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)
}
作者:Standby — 一生热爱名山大川、草原沙漠,还有我们小郭宝贝!
出处:http://www.cnblogs.com/standby/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
出处:http://www.cnblogs.com/standby/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

浙公网安备 33010602011771号