Go 平滑重啓
【導讀】線上業務對服務要求高,如何實現服務不強制停止,平滑重啓?本文介紹了一種 go 服務優雅退出的實現。
問題背景
生產環境重要且複雜,許多的操作需要在任何場景都要保證正常運行。
如果我們對線上服務進行更新的步驟如下:
-
kill -9
服務 -
再啓動服務
那麼將不可避免的出現以下兩個問題:
- 新服務啓動期間,請求無法進來,導致一段時間的服務不可用現象
一般有三種方案處理以上問題:
-
生產環境會通過四層 (lb)-> 七層 (gateway)-> 服務,那麼可以通過流量調度的方式實現平滑重啓
-
k8s 管理
-
程序自身完成平滑重啓(本章介紹)
什麼事平滑重啓
進程在不關閉其所監聽端口的情況下進行重啓,並且重啓的整個過程保證所有請求都能被正確處理。
主要步驟:
-
原進程(父進程)先
fork
一個子進程,同時讓fork
出來的子進程繼承父進程所監聽的socket
。 -
子進程完成初始化後,開始接收
socket
的請求。 -
父進程停止接收新的請求,並將當下的請求處理完,等待連接空閒後,平滑退出。
信號(Signal)
服務的平滑重啓,主要依賴進程接收的信號(實現進程間通信),這裏簡單的介紹Golang
中信號的處理:
發送信號
-
kill:命令允許用戶發送一個特定的信號給進程
-
raise:庫函數可以發送特定的信號給當前進程
在 Linux 下運行man kill
可以查看此命令的介紹和用法。
kill -- terminate or signal a process
The kill utility sends a signal to the processes specified by the pid operands.
Only the super-user may send signals to other users' processes.
常用信號類型
信號的默認行爲:
-
term:信號終止進程
-
core:產生核心轉儲文件並退出
-
ignore:忽略信號
-
stop:信號停止進程
-
cont:信號恢復一個已停止的進程
信號接收測試
package main
import (
"log"
"os"
"os/signal"
"syscall"
)
func main() {
sigs := make(chan os.Signal)
signal.Notify(sigs, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGUSR1, syscall.SIGUSR2)
// 監聽所有信號
log.Println("listen sig")
signal.Notify(sigs)
// 打印進程id
log.Println("PID:", os.Getppid())
s := <-sigs
log.Println("退出信號", s)
}
go run main.go
## --> listen sig
## --> PID: 4604
kill -s HUP 4604
# --> Hangup: 1
實現案例
demo:
func main() {
sigs := make(chan os.Signal)
signal.Notify(sigs, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGUSR1, syscall.SIGUSR2)
// 監聽所有信號
log.Println("listen sig")
signal.Notify(sigs)
// 打印進程id
log.Println("PID:", os.Getppid())
go func() {
for s := range sigs {
switch s {
case syscall.SIGHUP:
log.Println("startNewProcess...")
startNewProcess()
log.Println("shutdownParentProcess...")
shutdownParentProcess()
case syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT:
log.PrintLn("Program Exit...", s)
case syscall.SIGUSR1:
log.Println("usr1 signal", s)
case syscall.SIGUSR2:
log.Println("usr2 signal", s)
default:
log.Println("other signal", s)
}
}
}()
<-sigs
}
推薦組件
Facebookarchive/grace https://github.com/facebookarchive/grace
shutdown 優雅退出
go 1.8.x 後,golang 在 http 里加入了 shutdown 方法,用來控制優雅退出。
package main
import (
"context"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
s := http.NewServeMux()
s.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
time.Sleep(3 * time.Second)
log.Println(w, "Hello world!")
})
server := &http.Server{
Addr: ":8090",
Handler: s,
}
go server.ListenAndServe()
listenSignal(context.Background(), server)
}
func listenSignal(ctx context.Context, httpSrv *http.Server) {
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
select {
case <-sigs:
log.Println("notify sigs")
httpSrv.Shutdown(ctx)
log.Println("http shutdown")
}
}
轉自:WilburXu
segmentfault.com/a/1190000038461407
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/LcYdLEkJc1qMCn-LESzfkw