1. 程式人生 > 其它 >golang signal訊號處理

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")
}