让你的golang程序支持重启命令

2023-05-01  本文已影响0人  zhyuzh3d

能不能让Golang服务器程序运行后,输入r命令让程序一键自动重启?

交互命令重启

下面是个简单有效的办法(只是重新启动服务,不编译更新)

// 监听命令
func listenCmd() {
    f := "start.listenCmd"
    scanner := bufio.NewScanner(os.Stdin)
    for scanner.Scan() {
        switch scanner.Text() {
        case "r", "reset":
            restartZapp()
        case "q", "quit":
            os.Exit(0)
        }
    }
}

// 重启命令
func restartZapp() {
    executablePath, _ := os.Executable()
    syscall.Exec(executablePath, os.Args, os.Environ())
}

然后在你的main函数里面第一行添加go listenCmd()然后就成功了!程序启动之后,只要输入r就能完整重启。

root@iZuf612crf3p3310kvf8qgZ:~/zapp# go run zapp.go -port 80
[2023-05-02 05:05:05][start.createServer]:Starting server on:80...
r
[2023-05-02 05:05:05][start.listenCmd]:Restarting...
[2023-05-02 05:05:05][start.createServer]:Starting server on:80...

如何捕获启动参数,比如上面的-port 80

portPntr := flag.Int("port", conf.C.HttpPort, "Server listen on this port.")
flag.Parse()
Port= *portPntr

但这个方法不是重新编译运行Golang服务,只是把已经运行的程序重新启动一遍。

一键重新编译运行

因为服务程序需要避免端口重复被占用,所以服务中就要包含终止之前服务的代码,因此,如果在代码中重新启动自身,就会先把自己杀死,导致根本不会启动。

解决的办法是新建另外一个Golang程序launcher,用它来启动另外的myapp.go。代码如下。

package main
import (
    "bufio"
    "fmt"
    "os"
    "os/exec"
    "syscall"
)

func main() {
    fmt.Println("Launcher is ready...")
    scanner := bufio.NewScanner(os.Stdin)
    for scanner.Scan() {
        switch scanner.Text() {
        case "r", "reset":
            fmt.Println("Restarting...")
            os.Chdir("/root/zapp/")
            go run()
        case "q", "quit":
            fmt.Println("Quit...")
            os.Exit(0)
        }
    }
    select {}
}
func run() {
    cmd := exec.Command("go", "run", "myapp.go", "-port", "80")
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Foreground: false,
    }
    err := cmd.Start()
    if err != nil {
        fmt.Println("New Zapp start failed:", err)
        return
    } else {
        fmt.Println("New Zapp run successed.")
    }
    err = cmd.Wait()
    if err != nil {
        fmt.Println("Old Zapp ended:", err)
        return
    }
}

这个程序启动后,输入r回车就会自动运行 "myapp.go"文件,实现实时更新。但这个方法的缺点是不能直接切入程序看到程序输出。

最佳解决方案

使用IDE的快捷键直接重启程序可能是最好的办法,下面以VSCode为例,分三步:

1、从首选项打开快捷键设置,然后进入json配置文件编辑模式。


image.png

2、然后修改下面两个位置,其他不要动,text里可以\n就是多行命令,注意这里重复了两次go run命令,第一行是结束当前程序(见下一步),第二行才是启动新程序:

image.png
3、让你的程序支持交换命令退出,收到第一行go run之后就退出。方法是,添加下面函数,并在main中go listenCmd()首先运行它。
func listenCmd() {
    f := "start.listenCmd"
    scanner := bufio.NewScanner(os.Stdin)
    for scanner.Scan() {
        text := scanner.Text()
        switch {
        case strings.HasPrefix(text, "go run") || text == "q" || text == "quit":
            logger.Info(f, "Quit...")
            os.Exit(0)
        }
    }
}

成功之后,使用快捷键Cmd+D就会执行两次go run...第一行被正在运行的Golang程序捕获,Golang程序退出,第二行才是重新运行。首次执行会浪费一次,其实Golang编译超快也无所谓。

以上,就是实现一键重启Golang程序的可行性办法,有用的话请点赞收藏。

上一篇 下一篇

猜你喜欢

热点阅读