程式開發完畢之後,我們現在要部署 Web 應用程式了,但是我們如何來部署這些應用程式呢?因為 Go 程式編譯之後是一個可執行檔案,編寫過 C 程式的讀者一定知道採用 daemon 就可以完美的實現程式後臺持續執行,但是目前 Go 還無法完美的實現 daemon,因此,針對 Go 的應用程式部署,我們可以利用第三方工具來管理,第三方的工具有很多,例如 Supervisord、upstart、daemontools 等,這小節我介紹目前自己系統中採用的工具 Supervisord。
daemon
目前 Go 程式還不能實現 daemon,詳細的見這個 Go 語言的 bug:,大概的意思說很難從現有的使用的執行緒中 fork 一個出來,因為沒有一種簡單的方法來確保所有已經使用的執行緒的狀態一致性問題。
但是我們可以看到很多網上的一些實現 daemon 的方法,例如下面兩種方式:
MarGo 的一個實現思路,使用 Command 來執行自身的應用,如果真想實現,那麼推薦這種方案
d := flag.Bool("d", false, "Whether or not to launch in the background(like a daemon)")
if *d {
cmd := exec.Command(os.Args[0],
"-close-fds",
"-addr", *addr,
"-call", *call,
)
serr, err := cmd.StderrPipe()
if err != nil {
log.Fatalln(err)
}
err = cmd.Start()
if err != nil {
log.Fatalln(err)
}
s, err := ioutil.ReadAll(serr)
s = bytes.TrimSpace(s)
if bytes.HasPrefix(s, []byte("addr: ")) {
fmt.Println(string(s))
cmd.Process.Release()
} else {
log.Printf("unexpected response from MarGo: `%s` error: `%v`\n", s, err)
cmd.Process.Kill()
}
}
另一種是利用 syscall 的方案,但是這個方案並不完善:
package main
import (
"log"
"os"
"syscall"
)
func daemon(nochdir, noclose int) int {
var ret, ret2 uintptr
var err uintptr
darwin := syscall.OS == "darwin"
// already a daemon
if syscall.Getppid() == 1 {
return 0
}
// fork off the parent process
ret, ret2, err = syscall.RawSyscall(syscall.SYS_FORK, 0, 0, 0)
if err != 0 {
return -1
}
// failure
if ret2 < 0 {
os.Exit(-1)
}
// handle exception for darwin
if darwin && ret2 == 1 {
ret = 0
}
// if we got a good PID, then we call exit the parent process.
if ret > 0 {
os.Exit(0)
}
/* Change the file mode mask */
_ = syscall.Umask(0)
// create a new SID for the child process
s_ret, s_errno := syscall.Setsid()
if s_errno != 0 {
log.Printf("Error: syscall.Setsid errno: %d", s_errno)
}
if s_ret < 0 {
return -1
}
if nochdir == 0 {
os.Chdir("/")
}
if noclose == 0 {
f, e := os.OpenFile("/dev/null", os.O_RDWR, 0)
if e == nil {
fd := f.Fd()
syscall.Dup2(fd, os.Stdin.Fd())
syscall.Dup2(fd, os.Stdout.Fd())
syscall.Dup2(fd, os.Stderr.Fd())
}
}
return 0
}
上面提出了兩種實現 Go 的 daemon 方案,但是我還是不推薦大家這樣去實現,因為官方還沒有正式的宣佈支援 daemon,當然第一種方案目前來看是比較可行的,而且目前開源函式庫 skynet 也在採用這個方案做 daemon。
Supervisord
上面已經介紹了 Go 目前是有兩種方案來實現他的 daemon,但是官方本身還不支援這一塊,所以還是建議大家採用第三方成熟工具來管理我們的應用程式,這裡我給大家介紹一款目前使用比較廣泛的程序管理軟體:Supervisord。Supervisord 是用 Python 實現的一款非常實用的程序管理工具。supervisord 會幫你把管理的應用程式轉成 daemon 程式,而且可以方便的透過命令開啟、關閉、重啟等操作,而且它管理的程序一旦崩潰會自動重啟,這樣就可以保證程式執行中斷後的情況下有自我修復的功能。