zoukankan      html  css  js  c++  java
  • 守护进程

    实现基本的进程守护功能

    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
    ", 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
    ", 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
    ", selfPid, err)
    		return
    	} else {
    		fmt.Printf("[Daemon pid=%d]succeed start app process, app pid=%d
    ", 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...
    ", time.Now().Format("2006-01-02 15:04:05"))
    		case s := <-signals:
    			fmt.Printf("Daemon got siganl=%d, now[%s] to exit...
    ", 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
    ", bye.Cmd.Process.Pid, bye.Cmd.String(), bye.Error.Error())
    			}
    			fmt.Printf("
    %+v
    
    ", 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
    ", selfPid, err)
    				return
    			} else {
    				fmt.Printf("[Daemon pid=%d]succeed start app process, pid=%d
    ", 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...
    ", selfPid)
    }
    

      

    作者:Standby一生热爱名山大川、草原沙漠,还有妹子
    出处:http://www.cnblogs.com/standby/

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    268. Missing Number(LeetCode)
    Win7 发生验证错误 要求的函数不受支持
    下拉选择框的选项过滤
    金额小写带逗号处理
    Java将CST的时间字符串转换成需要的日期格式字符串
    MyEclipse部署项目报"Add Deployment". Invalid Subscription Level
    无法解析的DNS服务地址
    选择文件进行上传
    让button居中显示的的标签
    Mysql启动服务提示系统找不到指定的文件
  • 原文地址:https://www.cnblogs.com/standby/p/15246398.html
Copyright © 2011-2022 走看看