funcmain() { var ( err error listener net.Listener
// Pass the parameter on the command line to decide whether to fork a child process // If there is no parameter control, the child process will fork another process, // and the fork process will fork a child process. // This has been the fork child process, obviously this is not what we need fork = flag.Bool("f", false, "Parameter determines whether to fork a child process") ) flag.Parse()
server := http.Server{Handler: &Handler{}, ReadTimeout: 6 * time.Second} log.Println("Actual pid is ", syscall.Getpid()) log.Printf("Listener: %v \n", listener)
if *fork { // To make it easier to check the status of the process, fork child process after 10 seconds timer := time.NewTimer(10 * time.Second) gofunc() { <- timer.C tl, _ := listener.(*net.TCPListener) currentFd, err := tl.File() if err != nil { panic("acquiring listener file failed") } // Here, you need to remove the parent process's -f parameter to prevent the child process // from creating the child process. cmd := exec.Command(os.Args[0]) cmd.ExtraFiles, cmd.Stdout, cmd.Stderr = []*os.File{currentFd}, os.Stdout, os.Stderr err = cmd.Start() if err != nil { panic(fmt.Sprintf("cmd.Start fail:%s", err)) } fmt.Println("forked new pid: ", cmd.Process.Pid) }() }
funcmain() { var ( err error listener net.Listener // You need a wait group to wait for the current process (parent process) http service to shut down gracefully, // otherwise, after calling server.Shutdown, the main thread will stop blocking the entire process exit, // and the old requests that are not completed will be discarded // And this wait group must be a pointer (the data exists on the heap), // because we said before: The child process is the stack area of the shared parent process, // when the wait group exists on the stack, it will cause two processes to call a same one wait group group = new(sync.WaitGroup) graceful = flag.Bool("g", false, "When the service is started for the first time, "+ "there is no need to perform hot restart related operations") ) flag.Parse()
ListenSignal: for { sig := <-signalChan // Give the http service that needs to be gracefully shut down a timeout to // prevent the parent process from persisting ctx, _ := context.WithTimeout(context.Background(), 20*time.Second) switch sig { case syscall.SIGTERM, syscall.SIGHUP: tl, _ := listener.(*net.TCPListener) currentFd, err := tl.File() if err != nil { panic("acquiring listener file failed") } cmd := exec.Command(os.Args[0], "-g") cmd.ExtraFiles, cmd.Stdout, cmd.Stderr = []*os.File{currentFd}, os.Stdout, os.Stderr err = cmd.Start() if err != nil { panic(fmt.Sprintf("cmd.Start fail:%s", err)) } fmt.Println("forked new pid: ", cmd.Process.Pid)
// When the child process fork is complete, close the http service of the parent process, // so the parent process will not receive new requests. err = server.Shutdown(ctx) if err != nil { fmt.Println("shutdown fail: ", err) }
// After closing the parent process http service, // you need to jump out of the loop and stop monitoring the signal, // otherwise the parent process will still exist break ListenSignal } } }(group)
err = server.Serve(listener) if err != nil { log.Println(err) } fmt.Printf("Pid %d http server closed\n", os.Getpid())
// After calling server.Shutdown, server.Serve will stop blocking the main thread, // so here we need to wait for the old request to be processed or timed out group.Wait() }