go程序不停機重啟
- 2021 年 11 月 23 日
- 筆記
讓我們給http服務寫一個版本更新接口,讓它自動更新版本並重啟服務吧。
初步例子
註:為了精簡,文中代碼都去除了err處理
main.go
var Version = "1.0"
/* 打印版本 */
func version(w http.ResponseWriter, r *http.Request) {
msg := fmt.Sprintf("version %v\n", Version)
w.Write([]byte(msg))
}
/* 版本升級 */
func upgrade(w http.ResponseWriter, r *http.Request) {
// 1. 把新版本文件放置到服務主目錄(簡化)
os.Remove("test_restart")
os.Rename("new_test_restart", "test_restart")
// 2. go的可執行文件加權限(省略)
// 3. 重啟服務
cmd := exec.Command("/bin/bash", "-c", "./restart.sh")
cmd.Output()
w.Write([]byte("restart ok\n"))
}
func main() {
// 記錄pid
f,_ := os.Create("s.pid")
pid := os.Getpid()
f.WriteString(fmt.Sprintf("%v", pid))
fmt.Printf("System running:%v\n", pid)
// 監聽連接
mux := http.NewServeMux()
mux.HandleFunc("/version", version)
mux.HandleFunc("/upgrade", upgrade)
http.ListenAndServe("127.0.0.1:9527", mux)
}
restart.sh(重啟腳本)
kill -9 $(cat s.pid)
nohup ./test_restart > nohup.log 2>&1 &
測試
1. 編譯後開始運行
2. 請求一下版本信息接口
3. 將代碼中Version改為「1.1」, 生成一個新文件「new_test_restart」
4. 請求版本更新接口
發現問題
重啟腳本是stop後start的,stop時直接殺死了進程,程序直接中斷了當前所有的連接,此時接口函數還未return,導致調用方接收不到響應。
使用Endless
看來得不中斷已有連接的情況下進行重啟才行,不能簡單的stop後start,得平滑重啟。大致就是讓父進程啟動一個子進程去監聽新的連接,自己不再監聽新的連接,而是在處理完已有連接後終止,之後子進程獨挑大樑。
隨後發現github上的endless挺滿足需求,它是一個不停機重啟的服務器實現,實現流程為:
- 監聽 SIGHUP 信號
- 收到信號後 fork 子進程(使用相同的啟動命令),將服務監聽的 socket 文件描述符傳遞給子進程
- 子進程啟動成功後開始監聽新的連接,並發送 SIGTERM 信號給父進程
- 父進程收到 SIGTERM 信號後停止接收新的連接,等待舊連接處理完成後終止
- 父進程終止,重啟完成
關於 SIGHUP 信號,我們可以用「kill -1」命令發送給endless。
使用endless改動很小,在main函數中只需要把 http.ListenAndServe 修改為 endless.ListenAndServe即可:
main.go
func main() {
// 記錄pid(省略)
// 監聽連接
mux := http.NewServeMux()
mux.HandleFunc("/version", version)
mux.HandleFunc("/upgrade", upgrade)
// http.ListenAndServe("127.0.0.1:9527", mux)
endless.ListenAndServe("127.0.0.1:9527", mux)
}
restart.sh
kill -1 $(cat s.pid)
再測試
1. 編譯後開始運行(帶時間前綴的是endless打印的日誌)
2. 請求一下版本信息接口
3. 將代碼中Version改為「1.1」, 生成一個新文件「new_test_restart」
4. 請求版本更新接口
請求沒有被中斷,成功接收了響應。
endless的日誌比較清晰,77625的父進程接收 SIGHUP 後,fork了子進程77625,接收到子進程傳遞的 SIGHUP 後,等待已有連接處理完成後終止,完全符合上述介紹的流程。
5. 驗證版本
至此,不停機版本更新成功 ٩(◕‿◕。)۶