Ctrl+Cコンソールから送信された( SIGINT
) シグナルをキャプチャし、部分的な実行合計を出力したいと考えています。
これはGolangで可能ですか?
注: 最初に質問を投稿したとき、私はCtrl+C代わりSIGTERM
にSIGINT
.
os / signalパッケージを使用して、着信信号を処理できます。Ctrl+CはSIGINTなので、これを使用してトラップできますos.Interrupt
。
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func(){
for sig := range c {
// sig is a ^C, handle it
}
}()
プログラムを終了させて情報を印刷する方法は、完全にあなた次第です。
他の回答に少し追加すると、実際に SIGTERM (kill コマンドによって送信されるデフォルトのシグナル) をキャッチしたい場合はsyscall.SIGTERM
、os.Interrupt の代わりに使用できます。syscall インターフェイスはシステム固有のものであり、どこでも機能するとは限らないことに注意してください (例: Windows)。ただし、両方をキャッチするとうまく機能します。
c := make(chan os.Signal, 2)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
....
上記の受け入れられた回答には(投稿時に)1つまたは2つの小さなタイプミスがあったため、クリーンアップされたバージョンを次に示します。この例では、Ctrl+を受け取ったときに CPU プロファイラーを停止していますC。
// capture ctrl+c and stop CPU profiler
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
for sig := range c {
log.Printf("captured %v, stopping profiler and exiting..", sig)
pprof.StopCPUProfile()
os.Exit(1)
}
}()
このプログラムを実行すると、シグナル待ちでブロックされます。ctrl-C (ターミナルでは ^C と表示されます) を入力すると、SIGINT シグナルを送信でき、プログラムは割り込みを出力して終了します。
信号。Notify は、指定されたチャネルを登録して、指定されたシグナルの通知を受信します。
package main
import (
"fmt"
"os"
"os/signal"
"syscall"
)
func main() {
sig := make(chan os.Signal, 1)
done := make(chan bool, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
go func() {
sig := <-sig
fmt.Println()
fmt.Println(sig)
done <- true
fmt.Println("ctrl+c")
}()
fmt.Println("awaiting signal")
<-done
fmt.Println("exiting")
}
HTTPリクエストのキャンセルを検出
package main
import (
"fmt"
"net/http"
"time"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/path", func(writer http.ResponseWriter, request *http.Request) {
time.Sleep(time.Second * 5)
select {
case <-time.After(time.Millisecond * 10):
fmt.Println("started")
return
case <-request.Context().Done():
fmt.Println("canceled")
}
})
http.ListenAndServe(":8000", mux)
}
これは、クリーンアップするタスクがある場合に機能する別のバージョンです。コードは、メソッドにクリーンアップ プロセスを残します。
package main
import (
"fmt"
"os"
"os/signal"
"syscall"
)
func main() {
_,done1:=doSomething1()
_,done2:=doSomething2()
//do main thread
println("wait for finish")
<-done1
<-done2
fmt.Print("clean up done, can exit safely")
}
func doSomething1() (error, chan bool) {
//do something
done:=make(chan bool)
c := make(chan os.Signal, 2)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
//cleanup of something1
done<-true
}()
return nil,done
}
func doSomething2() (error, chan bool) {
//do something
done:=make(chan bool)
c := make(chan os.Signal, 2)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
//cleanup of something2
done<-true
}()
return nil,done
}
メイン関数をクリーンアップする必要がある場合は、go func() も使用してメインスレッドでシグナルをキャプチャする必要があります。
syscall.SIGINT および syscall.SIGTERM シグナルを検出し、 signal.Notify を使用してチャネルに中継する別のゴルーチンを使用できます。チャネルを使用してそのゴルーチンにフックを送信し、それを関数スライスに保存できます。チャネルでシャットダウン信号が検出されると、スライスでこれらの関数を実行できます。これは、リソースのクリーンアップ、実行中のゴルーチンの終了の待機、データの永続化、または部分的な実行合計の出力に使用できます。
シャットダウン時にフックを追加して実行するための小さくてシンプルなユーティリティを作成しました。それが助けになることを願っています。
https://github.com/ankit-arora/go-utils/blob/master/go-shutdown-hook/shutdown-hook.go
これは「延期」方式で行うことができます。
サーバーを正常にシャットダウンする例:
srv := &http.Server{}
go_shutdown_hook.ADD(func() {
log.Println("shutting down server")
srv.Shutdown(nil)
log.Println("shutting down server-done")
})
l, err := net.Listen("tcp", ":3090")
log.Println(srv.Serve(l))
go_shutdown_hook.Wait()