关于优雅关闭 grace shutdown 网上有很多例子,特别是 http 服务的优雅关闭/重启,但这里讲的是非在线应用优雅关闭。
比如一个简单的运算程序,不管运算有没有结束,只管监听系统中断信号。
首先定义一个 gracefulShutdown
函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// operation is a cleanup function on shutting down
type operation func(ctx context.Context) error
// gracefulShutdown waits for termination syscalls and doing clean up operations after received it
func gracefulShutdown(ctx context.Context, timeout time.Duration, ops map[string]operation) <-chan struct{} {
wait := make(chan struct{})
go func() {
s := make(chan os.Signal, 1)
// add any other syscalls that you want to be notified with
signal.Notify(s, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
<-s
log.Println("shutting down")
// set timeout for the ops to be done to prevent system hang
timeoutFunc := time.AfterFunc(timeout, func() {
log.Printf("timeout %d ms has been elapsed, force exit", timeout.Milliseconds())
os.Exit(0)
})
defer timeoutFunc.Stop()
var wg sync.WaitGroup
// Do the operations asynchronously to save time
for key, op := range ops {
wg.Add(1)
innerOp := op
innerKey := key
go func() {
defer wg.Done()
log.Printf("cleaning up: %s", innerKey)
if err := innerOp(ctx); err != nil {
log.Printf("%s: clean up failed: %s", innerKey, err.Error())
return
}
log.Printf("%s was shutdown gracefully", innerKey)
}()
}
wg.Wait()
close(wait)
}()
return wait
}
然后在 main
函数里引用,比如下面简单的例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
func main() {
// initialize some resources
// e.g:
// db, err := database.Initialize()
// server, err := http.Initialize()
db, err := sdb.Open("db", &opt.Options{
Filter: filter.NewBloomFilter(10), // 一般取10
})
if err != nil {
log.Fatalf("Connect Error: %v", err)
}
// wait for termination signal and register database & http server clean-up operations
wait := gracefulShutdown(context.Background(), 8*time.Second, map[string]operation{
"database": func(ctx context.Context) error {
log.Println("db close()")
return db.Close()
},
//"http-server": func(ctx context.Context) error {
// return server.Shutdown()
//},
// Add other cleanup operations here
})
go func() {
for {
fmt.Println("go -bg")
time.Sleep(5 * time.Second)
fmt.Println("go -ed")
}
}()
log.Println("wait...")
<-wait
log.Println("main done")
}
当按 control + c
时会输出
1
2
3
4
5
6
7
8
9
10
% go run graceful_shutdown.go
2021/10/09 22:57:53 wait...
go -bg
go -ed
go -bg
^C2021/10/09 22:57:59 shutting down
2021/10/09 22:57:59 cleaning up: database
2021/10/09 22:57:59 db close()
2021/10/09 22:57:59 database was shutdown gracefully
2021/10/09 22:57:59 main done
这种关闭的重点是对相关重要资源(如数据库)作正常关闭,适用非正常关闭就容易出错的资源链接。
本文网址: https://golangnote.com/topic/290.html 转摘请注明来源