英文:
Golang: How to auto-restart process when binary updated?
问题
我在本地构建了Go语言应用程序,然后使用scp
命令将其传输到服务器上。我需要手动停止进程并重新启动。是否有一种方法可以在二进制文件更新时自动重新启动进程?
英文:
I built the golang app in local, then scp
to server. I need to stop the process and restart manually. Is there any way to auto-restart the process when binary updated?
答案1
得分: 6
虽然通常最好使用类似daemontools或类似工具在进程外部实现此功能,但有些情况下您可能希望在程序内部完成。
在程序内部实现可能会有一些复杂性,取决于程序的特性,例如它可能打开的连接或文件等。
话虽如此,下面是一个在大多数情况下都能工作的实现示例:
package main
import (
"log"
"os"
"syscall"
"time"
"github.com/fsnotify/fsnotify"
"github.com/kardianos/osext"
)
func setupWatcher() (chan struct{}, error) {
file, err := osext.Executable()
if err != nil {
return nil, err
}
log.Printf("watching %q\n", file)
w, err := fsnotify.NewWatcher()
if err != nil {
return nil, err
}
done := make(chan struct{})
go func() {
for {
select {
case e := <-w.Events:
log.Printf("watcher received: %+v", e)
err := syscall.Exec(file, os.Args, os.Environ())
if err != nil {
log.Fatal(err)
}
case err := <-w.Errors:
log.Printf("watcher error: %+v", err)
case <-done:
log.Print("watcher shutting down")
return
}
}
}()
err = w.Add(file)
if err != nil {
return nil, err
}
return done, nil
}
func main() {
log.Print("program starting")
watcher, err := setupWatcher()
if err != nil {
// do something sensible
log.Fatal(err)
}
// continue with app startup
time.Sleep(100 * time.Minute) // just for testing
// eventually you may need to end the watcher
close(watcher) // this way you can
}
然后执行以下命令:
% go build main.go
% ./main
2016/12/29 14:15:06 program starting
2016/12/29 14:15:06 watching "/home/plalloni/tmp/v/main"
在另一个终端窗口中运行连续的"go build main.go"命令("更新"正在运行的二进制文件),下面是它产生的输出:
2016/12/29 14:15:32 watcher received: "/home/plalloni/tmp/v/main": CHMOD
2016/12/29 14:15:32 program starting
2016/12/29 14:15:32 watching "/home/plalloni/tmp/v/main"
2016/12/29 14:15:38 watcher received: "/home/plalloni/tmp/v/main": CHMOD
2016/12/29 14:15:38 program starting
2016/12/29 14:15:38 watching "/home/plalloni/tmp/v/main"
希望对您有所帮助。
英文:
While this is generally better to be implemented off-process using something like daemontools or similar, there are some cases when you want/need it to be done inside your program.
Doing it inside your program can be tricky depending on the program characteristics such as connections or files it may have open, etc.
Having said that, here you have an implementation which would work in most cases:
package main
import (
"log"
"os"
"syscall"
"time"
"github.com/fsnotify/fsnotify"
"github.com/kardianos/osext"
)
func setupWatcher() (chan struct{}, error) {
file, err := osext.Executable()
if err != nil {
return nil, err
}
log.Printf("watching %q\n", file)
w, err := fsnotify.NewWatcher()
if err != nil {
return nil, err
}
done := make(chan struct{})
go func() {
for {
select {
case e := <-w.Events:
log.Printf("watcher received: %+v", e)
err := syscall.Exec(file, os.Args, os.Environ())
if err != nil {
log.Fatal(err)
}
case err := <-w.Errors:
log.Printf("watcher error: %+v", err)
case <-done:
log.Print("watcher shutting down")
return
}
}
}()
err = w.Add(file)
if err != nil {
return nil, err
}
return done, nil
}
func main() {
log.Print("program starting")
watcher, err := setupWatcher()
if err != nil {
// do something sensible
log.Fatal(err)
}
// continue with app startup
time.Sleep(100 * time.Minute) // just for testing
// eventually you may need to end the watcher
close(watcher) // this way you can
}
Then you do
% go build main.go
% ./main
2016/12/29 14:15:06 program starting
2016/12/29 14:15:06 watching "/home/plalloni/tmp/v/main"
And here the output it produced after you run (in other terminal) some successive "go build main.go" (which "updates" the running binary).
2016/12/29 14:15:32 watcher received: "/home/plalloni/tmp/v/main": CHMOD
2016/12/29 14:15:32 program starting
2016/12/29 14:15:32 watching "/home/plalloni/tmp/v/main"
2016/12/29 14:15:38 watcher received: "/home/plalloni/tmp/v/main": CHMOD
2016/12/29 14:15:38 program starting
2016/12/29 14:15:38 watching "/home/plalloni/tmp/v/main"
Hope it helps.
答案2
得分: 3
你可以使用https://github.com/slayer/autorestart。
package main
import "github.com/slayer/autorestart"
func main() {
autorestart.StartWatcher()
http.ListenAndServe(":8080", nil) // 例如
}
英文:
You can use https://github.com/slayer/autorestart
package main
import "github.com/slayer/autorestart"
func main() {
autorestart.StartWatcher()
http.ListenAndServe(":8080", nil) // for example
}
答案3
得分: 2
需要复杂吗?你可以运行entr
并在二进制文件更改时触发一个更新脚本。
例如:
echo 'binary_path' | entr script.sh &
英文:
Does it need to be sophisticated? You could have entr
running and trigger an updater script when the binary changes.
e.g.
echo 'binary_path' | entr script.sh &
答案4
得分: 1
我对这个案例有一个解决方案。
另请参阅。
https://github.com/narita-takeru/cmdrevive
示例
cmdrevive ./htmls/ ".html$" (应用程序) (参数)
所以,这个案例适用。
cmdrevive "/(应用程序路径)" "(应用程序文件名)" (应用程序完整路径) (参数)
如果在(应用程序路径)目录下更改了(应用程序文件名),则使用(参数)重新启动(应用程序完整路径)。
这个方案怎么样?
英文:
I have a resolution about this case.
See also.
https://github.com/narita-takeru/cmdrevive
example
cmdrevive ./htmls/ ".html$" (application) (arguments)
So, this case applicable.
cmdrevive "/(app path)" "(app filename)" (app full path) (arguments)
If (app filename) changed on (app path) directory, then restart (app full path) with (arguments).
How about this one?
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论