A warm restart
Zero Downtime: Old and new processes can be switched seamlessly, and client services can be maintained during replacement.
The principle of
- The parent process listens for a restart signal
- On receiving a restart signal, the parent process calls
fork
At the same timesocket
Descriptor to child process - The child receives and listens for the parent
socket
The descriptor - After the child successfully starts, the parent stops receiving new connections while waiting for the old connection to complete (or time out)
- The parent process exits, and the hot restart is complete
implementation
package main
import (
"context"
"errors"
"flag"
"log"
"net"
"net/http"
"os"
"os/exec"
"os/signal"
"syscall"
"time"
)
var (
server *http.Server
listener net.Listener = nil
graceful = flag.Bool("graceful".false."listen on fd open 3 (internal use only)")
message = flag.String("message"."Hello World"."message to send"))func handler(w http.ResponseWriter, r *http.Request) {
time.Sleep(5 * time.Second)
w.Write([]byte(*message))
}
func main(a) {
var err error
// Parse the parameters
flag.Parse()
http.HandleFunc("/test", handler)
server = &http.Server{Addr: ": 3000"}
// Set listener listener (new or existing socket descriptor)
if *graceful {
// The child listens for the socket descriptor passed by the parent
log.Println("listening on the existing file descriptor 3")
// The child process's 0, 1, 2 are reserved for standard input, standard output, and error output, so the socket descriptor passed
// should be placed in 3 of the child process
f := os.NewFile(3."")
listener, err = net.FileListener(f)
} else {
// The parent process listens for the newly created socket descriptor
log.Println("listening on a new file descriptor")
listener, err = net.Listen("tcp", server.Addr)
}
iferr ! =nil {
log.Fatalf("listener error: %v", err)
}
go func(a) {
err = server.Serve(listener)
log.Printf("server.Serve err: %v\n", err)
}()
// Listen for signals
handleSignal()
log.Println("signal end")}func handleSignal(a) {
ch := make(chan os.Signal, 1)
// Listen for signals
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR2)
for {
sig := <-ch
log.Printf("signal receive: %v\n", sig)
ctx, _ := context.WithTimeout(context.Background(), 20*time.Second)
switch sig {
case syscall.SIGINT, syscall.SIGTERM: // Terminates process execution
log.Println("shutdown")
signal.Stop(ch)
server.Shutdown(ctx)
log.Println("graceful shutdown")
return
case syscall.SIGUSR2: // The process restarts hot
log.Println("reload")
err := reload() // Execute the hot restart function
iferr ! =nil {
log.Fatalf("graceful reload error: %v", err)
}
server.Shutdown(ctx)
log.Println("graceful reload")
return}}}func reload(a) error {
tl, ok := listener.(*net.TCPListener)
if! ok {return errors.New("listener is not tcp listener")}// Get the socket descriptor
f, err := tl.File()
iferr ! =nil {
return err
}
// Set the parameters to be passed to the child process (including socket descriptors)
args := []string{"-graceful"}
cmd := exec.Command(os.Args[0], args...)
cmd.Stdout = os.Stdout // Standard output
cmd.Stderr = os.Stderr // Error output
cmd.ExtraFiles = []*os.File{f} // File descriptor
// Create and execute a child process
return cmd.Start()
}
Copy the code
We run cmd.extrafiles = []* os.file {f} in the parent process to pass the socket descriptor to the child process, which obtains the descriptor by executing f := os.newfile (3, “”). Note that 0, 1, and 2 of the child processes are reserved for standard input, standard output, and error output respectively, so the socket descriptor passed by the parent process starts at 3 in the child process order.
test
Compile the program as the main, execute. / the main – the message “Graceful” Reload, visit http://localhost:3000/test, wait 5 seconds later, we can see that the Graceful Reload response.
To perform a Graceful Reload test, run the kill -usr2 [PID] command.
Run the kill -int [PID] command to perform a Graceful Shutdown test.
The resources
- Gracehttp: Gracefully restart the Go program
- Golang server hot restart, hot upgrade, hot update details