A problem about fork

I’ve been stucked with this issue for days:

I listen a port with a fd in master process. Then I fork a new child process which inhert the fd.

I don’t want to kill the master process, and How can I accept connections no mattter in master process or child process???

I has try somes: If master process is alive, only it can accept connection; it is killed, child do it.

My golang’s version is 1.6.2.

Thx, Look forward to your reply!!!

Can you please show some code.

I want to gracefull restart golang server.I
listen a fd in master process, fork a child process inherit the fd and
only the child accept connection.When i restart, I want to firstly fork
new master process and child process which inherit the listen fd from
old master’s listen fd.Do this, in any time listen port is alive. And
why two processes alive: I want to master monitor child, if child
process crash, master process can start a new child process inmediately.

some code:

var parent_flag = flag.Bool("P", false, "Parent Watch Mode")
var fork_flag = flag.Bool("F", false, "Process forked")

var msgListenFd uintptr
var rpcListenFd uintptr

func InitListener() {
    //conf load
    conf.LoadConf()
    msgOpts := conf.GetMsgServiceOpts()
    msgsrv.ListenNotServe(msgOpts.ListenAddr)
    msgListenFd = msgsrv.GetListenFd()
    logger.Errorf(nil, "msgListenFd:%v", msgListenFd)
    rpcServiceOpts := conf.GetRpcServiceOpts()
    rpcsrv.ListenNotServe(rpcServiceOpts.ListenAddr)
    rpcListenFd = rpcsrv.GetListenFd()
    logger.Errorf(nil, "rpcListenFd:%v", rpcListenFd)
}

func InitListenerInGraceful() {
    msgsrv.ListenWithFork(3)
    rpcsrv.ListenWithFork(4)
}

func StartServe() {
    msgsrv.StartServe()
    rpcsrv.StartServe()
}

func main() {
        if *fork_flag {
            InitListenerInGraceful()
        } else {
            InitListener()
        }
        go ParentSignalMonitor()
        var failCount int = 0
        for {
            var proc_attr syscall.ProcAttr
            var sys_attr syscall.SysProcAttr
            var waitStatus syscall.WaitStatus
            tNow := time.Now()
            stdErrFileName := fmt.Sprintf("logs/cgc.log.%04d%02d%02d%02d%02d%02d", tNow.Year(),
                tNow.Month(), tNow.Day(), tNow.Hour(), tNow.Minute(), tNow.Second())
            stdErrFile, err := os.OpenFile(stdErrFileName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
            //if err != nil {
            logger.Errorf(nil, "os.OpenFile,err:%v", err)
            //}
            sys_attr.Ptrace = false
            proc_attr.Sys = &sys_attr
            proc_attr.Files = []uintptr{uintptr(syscall.Stdin), uintptr(syscall.Stdout), stdErrFile.Fd(), msgListenFd, rpcListenFd}
            proc_attr.Env = os.Environ()
            pid, err := syscall.ForkExec(os.Args[0], append(append(os.Args[0:i], os.Args[i+1:]...), "-F"), &proc_attr)
            pid2, err2 := syscall.ForkExec(os.Args[0], append(append(os.Args[0:i], os.Args[i+1:]...), "-F"), &proc_attr)
            os.Exit(0)
            childPid = pid
            //if err != nil {
            logger.Errorf(nil, "syscall.ForkExec:err:%v", err)
            logger.Errorf(nil, "syscall.ForkExec:err:%v,%v", err2, pid2)
            //}
            //fmt.Printf("syscall.ForkExec:err:%v", err)
            syscall.Wait4(pid, &waitStatus, 0, nil)
            if time.Now().Sub(tNow).Seconds() < float64(30) {
                failCount++
            } else {
                failCount = 0
            }
            if failCount > 5 {
                logger.Errorf(nil, "Parent process exited. failCount > 5")
                //quit
                return
            }
            if stopping {
                logger.Errorf(nil, "Parent process exited.")
                return
            }
        }
    } else {
        if *fork_flag {
            InitListenerInGraceful()
        } else {
            logger.Warningf(nil, "InitListener...")
            InitListener()
        }
        err := Init()
        if err != nil {
            logger.Fatal(nil, err)
        }
        if *fork_flag {
            ppid := os.Getppid()
            syscall.Kill(ppid, syscall.SIGUSR1)
        } else {
            StartServe()
        }
        SignalMonitor()
        Stop()
    }
}

I don’t think you should do this in Go. Process management is the job of your init system, you should not try to write this code in Go.

But how do i implement gracefull restart. Please get me a solution.Thanks very much! :joy:

What do you want to achieve ?

Do you want to restart your program quickly - ie, people coming to your website do not see that your site is down ?

-or-

Do you want to replace the running program with another one, and the old version finishes handling the requests it has been assigned ?

first

I don’t think you need to do anything special here. Assuming you are using a modern init system (upstart, systemd, daemontools, etc) then there is nothing special you need to do here. Create a configuration file for your init system that will restart your program if it ever stops, have it run your program, and then when you want to upgrade it, replace the binary on disk and kill the running version, the init system will restart it.

2 Likes

thx, Nice~

Also see https://github.com/facebookgo/grace & https://github.com/zenazn/goji/tree/master/graceful

Matt, thank you.

i guess you need to implement some IPC (use signals and maybe semaphores for). your master process must sent a shutdown signal to child.

Thank you. Yes, I use signals. I made some mistake and now code is ok.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.