pidproxy 实现

参考python的supervisor pidproxy实现
https://github.com/Supervisor/supervisor/blob/master/supervisor/pidproxy.py
子进程检查

package pidproxy

import (
	"errors"
	"io/ioutil"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/spf13/cobra"
)

func getPid(pidfile string) (int, error) {
	dt, err := ioutil.ReadFile(pidfile)
	if err != nil {
		return -1, err
	}
	if len(dt) == 0 {
		return -1, errors.New("pid is null")
	}

	pid, err := strconv.Atoi(string(dt))
	if err != nil {
		return -1, err
	}
	return pid, err
}

func GetCmd() *cobra.Command {
	var signals = []os.Signal{
		syscall.SIGHUP,
		syscall.SIGUSR1,
		syscall.SIGUSR2,
		syscall.SIGTSTP,
		syscall.SIGINT,
		syscall.SIGTERM,
		syscall.SIGQUIT,
		syscall.SIGCHLD,
	}
	var sigChan = make(chan os.Signal)
	var pid = -1
	var pidfile = ""
	var forkExec = func(argv0 string, argv []string) (pid int, err error) {
		return syscall.ForkExec(argv0, argv, &syscall.ProcAttr{
			Env:   os.Environ(),
			Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()},
		})
	}
	return &cobra.Command{
		Use:   "pp",
		Short: "Run as a pidproxy server",
		Example: `dio pp <pidfile name> "<command> [<cmdarg1> ...]"
dio pp test.pid "/usr/local/bin/etcd --name=default"`,
		RunE: func(cmd *cobra.Command, args []string) (err error) {
			if len(args) < 2 {
				return errors.New("请输入pidfile和需要执行的命令以及参数")
			}

			if strings.TrimSpace(args[1]) == "" {
				return errors.New("command命令为空")
			}

			command := strings.Split(args[1], " ")
			pid, err = forkExec(command[0], command)
			if err != nil {
				return err
			}

			// 防止子进程变成僵尸进程
			go func(pid int) {
				for {
					_, _ = syscall.Wait4(pid, nil, syscall.WNOWAIT, nil)
					time.Sleep(time.Second * 5)
					return
				}
			}(pid)

			pidfile = args[0]
			if err := ioutil.WriteFile(pidfile, []byte(strconv.Itoa(pid)), 0600); err != nil {
				return err
			}

			signal.Notify(sigChan, signals...)
			var tk = time.Tick(time.Second * 2)
			var sig os.Signal
			for {
				select {
				// pidproxy进程获取信号
				case sig = <-sigChan:
				// 定时监控子进程状态
				case <-tk:
					// 信号0一般用来检查进程是否存在, 没有具体的意义
					sig = syscall.Signal(0)
				}

				// 获取子命令pid
				if pid, err = getPid(pidfile); err != nil {
					return err
				}

				// 校验pid是否存在
				proc, err := os.FindProcess(pid)
				if err != nil {
					return err
				}

				// 给子命令发送信号, 如果程序有问题, 就退出重启
				if err := proc.Signal(sig); err != nil {
					return err
				}

				switch sig {
				case syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT:
					os.Exit(0)
				}
			}
		},
	}
}
posted @ 2020-07-30 10:13  白云辉  阅读(425)  评论(0)    收藏  举报
编辑推荐:
· 于是转身独立开发者
· C#.Net筑基-泛型T & 协变逆变
· dotnet 代码调试方法
· DbContext是如何识别出实体集合的
· 一次 .NET 性能优化之旅:将 GC 压力降低 99%
阅读排行:
· 免费开源 .NET OpenCV 迷你运行时全平台发布
· 10亿订单如何分库分表?
· 一个static关键字引发的线上故障:深度剖析静态变量与配置热更新的陷阱
· C# 的深度强化学习框架RL_Matrix
· 如何基于three.js(webgl)引擎架构,实现3D医院、3D园区导航,3D科室路径导航
点击右上角即可分享
微信分享提示