golang signal訊號處理
1、使用場景
實際專案中,我們希望修改了配置檔案後,但又不想通過重啟程序讓它重新載入配置檔案,可以使用signal的方式進行訊號傳遞,或者我們希望通過訊號控制,實現一種優雅的退出方式。Golang為我們提供了signal包,實現訊號處理機制,允許Go 程式與傳入的訊號進行互動。
2、常用的Term訊號
3、簡單的栗子
package main import ( "fmt" "os" "os/signal" ) func main() { c := make(chan os.Signal) signal.Notify(c) fmt.Println("start..") s := <-c fmt.Println("End...", s) }
1)傳遞SIGINT訊號
[homework@xxxxx signal]$ go run monitor.go
start..
#此時,CTL+C傳送一個SIGINT訊號量,得到輸出為:
[homework@xxxxx signal]$ go run monitor.go
start..
^CEnd... interrupt
(2)傳遞SIGTERM訊號
開啟2個Term視窗
第一個執行go run monitor.go程式
第二個執行:ps -ef | grep monitor.go | grep grep -v | awk '{print $2}' | xargs kill
#此時,kill命令傳送一個SIGTERM訊號量,得到輸出為:
[homework@xxxxx signal]$ go run monitor.go
start..
Terminated
4、優雅的退出守護程序
(1)何為優雅(graceful)?
Linux Server端的應用程式經常會長時間執行,在執行過程中,可能申請了很多系統資源,也可能儲存了很多狀態。
在這些場景下,我們希望程序在退出前,可以釋放資源或將當前狀態dump到磁碟上或列印一些重要的日誌,即希望程序優雅退出。
(2)從對優雅退出的理解不難看出:優雅退出可以通過捕獲SIGTERM來實現。
A、註冊SIGTERM訊號的處理函式並在處理函式中做一些程序退出的準備,訊號處理函式的註冊sigaction()來實現。
B、在主程序的main()中,通過類似於while(!fQuit)的邏輯來檢測那個flag變數,一旦fQuit在signal handler function中被置為true,則主程序退出while()迴圈,接下來就是一些釋放資源或dump程序當前狀態或記錄日誌的動作,完成這些後,主程序退出。
栗子:優雅退出go守護程序
package main import ( "fmt" "os" "os/signal" "syscall" "time" ) func main() { //建立監聽退出chan c := make(chan os.Signal) //監聽指定訊號 ctrl+c kill signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGUSR1, syscall.SIGUSR2) go func() { for s := range c { switch s { case syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT: fmt.Println("Program Exit...", s) GracefullExit() case syscall.SIGUSR1: fmt.Println("usr1 signal", s) case syscall.SIGUSR2: fmt.Println("usr2 signal", s) default: fmt.Println("other signal", s) } } }() fmt.Println("Program Start...") sum := 0 for { sum++ fmt.Println("sum:", sum) time.Sleep(time.Second) } } func GracefullExit() { fmt.Println("Start Exit...") fmt.Println("Execute Clean...") fmt.Println("End Exit...") os.Exit(0) }
5、訊號的訂閱
訊號的訂閱是通過 channel實現的,每個os.Signal channel 都會收聽自己相應的事件集。
。golang中對訊號的處理主要使用os/signal包中的兩個方法:一個是notify方法用來監聽收到的訊號;一個是 stop方法用來取消監聽。
監聽訊號
notify方法原型
func Notify(c chan<- os.Signal, sig ...os.Signal)
第一個引數表示接收訊號的管道
第二個及後面的引數表示設定要監聽的訊號,如果不設定表示監聽所有的訊號。
Sometimes we'd like our Go programs to intelligently handle Unix signals. For example, we might want a server to gracefully shutdown when it receives a SIGTERM, or a command-line tool stop processing input if it receives a SIGINT. Here's how to handle signals in Go with channels
package main import ( "fmt" "os" "os/signal" "syscall" ) func main() { sigs := make(chan os.Signal, 1) done := make(chan bool, 1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) go func() { sig := <-sigs fmt.Println("") fmt.Println(sig) done <- true }() fmt.Println("awaiting signal") <-done fmt.Println("exiting") }