go程序不停機重啟


讓我們給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挺滿足需求,它是一個不停機重啟的服務器實現,實現流程為:

  1. 監聽 SIGHUP 信號
  2. 收到信號后 fork 子進程(使用相同的啟動命令),將服務監聽的 socket 文件描述符傳遞給子進程
  3. 子進程啟動成功后開始監聽新的連接,並發送 SIGTERM 信號給父進程
  4. 父進程收到 SIGTERM 信號后停止接收新的連接,等待舊連接處理完成后終止
  5. 父進程終止,重啟完成

    關於 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的日志比較清晰,105300的父進程接收 SIGHUP 后,fork了子進程105335,接收到子進程傳遞的 SIGHUP 后,等待已有連接處理完成后終止,完全符合上述介紹的流程。

    5. 驗證版本

   

    至此不停機版本更新成功 ٩(◕‿◕。)۶

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM