iris 服务器热重启(Go 语言)
2019-02-28 19:31
#旧文章
提示
我使用的服务器框架是 iris,其他服务器框架可以按照原理类推。
废话少说,直接进入正题。Go 是编译型语言,所以用一个主程序来热加载服务器的想法基本不可行。
热重启标志
想要热重启,我们先得让程序知道自己是人工启动的还是因热重启被启动的。在程序入口中加入一个 flag
isReload := flag.Bool("r", false, "Internal use, please don't use option")
然后传递给启动服务器的函数
func startServer(isReload bool) { app := iris.New() // 确定监听方式 var listener net.Listener var err error if !isReload { // 正常启动 listener, err = net.Listen("tcp", fmt.Sprintf("%s:%d", config.Settings.DomainName, config.Settings.ServerPort)) } else { // 热重启 mylog.Info("Reloading server", "[APP]") file := os.NewFile(3, "") // 获取重启所需的套接字文件 listener, err = net.FileListener(file) } if err != nil { mylog.Error(err.Error(), "[APP]") return } // 注册热重启钩子 go regSignal(app, &listener)
_ = app.Run( iris.Listener(listener), // 使用上面得到的Listener )}
其中 os.NewFile(3, "")
中的 3 是下文启动新进程时赋予的文件编号
热重启信号
在服务器上,我们可以通过发送信号给进程来实现命令下达,我们只需要在程序中监听 SIGUSR1
或 SIGUSR2
就能实现热重启命令的下达。
func regSignal(app *iris.Application, listener *net.Listener) { ch := make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGUSR2) // 监听SIGUSR2信号 select { case <-ch: // 收到信号,开始热重启 mylog.Info("SIGUSR2 received, zdt reloading server.", "[APP]") l, ok := (*listener).(*net.TCPListener) if !ok { mylog.Error("Listener isn't TCPListener.", "[APP]") return } f, err := l.File() if err != nil { mylog.Error(err.Error(), "[APP]") return } // 生成启动参数 size := len(os.Args) args := make([]string, size) needReloadFlag := true for i := 1; i < size; i++ { if os.Args[i] == "-r" { needReloadFlag = false } args[i-1] = os.Args[i] } if needReloadFlag { args[size-1] = "-r" } // 启动新进程 cmd := exec.Command(os.Args[0], args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.ExtraFiles = []*os.File{f} // 把监听套接字传给新进程 err = cmd.Start() // 结束当前服务 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() _ = app.Shutdown(ctx) return }}
现在就可以通过 kill
命令传递信号来实现热重启了
kill -s SIGUSR2 $PID