GolangNote

Golang笔记

Golang 程序优雅关闭/grace shutdown 的实际应用

Permalink

关于优雅关闭 grace shutdown 网上有很多例子,特别是 http 服务的优雅关闭/重启,但这里讲的是非在线应用优雅关闭。

比如一个简单的运算程序,不管运算有没有结束,只管监听系统中断信号。

首先定义一个 gracefulShutdown 函数

Go: 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 函数里引用,比如下面简单的例子

Go: main grace shutdown
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 时会输出

Bash: output
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 转摘请注明来源

Related articles

Golang 把cookie 字符串解析为cookie 结构

在做爬虫时有时候会遇到需要带已登录的 cookie 请求,这个时候最简单的方法是在浏览器登录后,在开发者面板找到cookie 字符串,然后拷贝粘贴。这就面临一个问题需要把cookie 字符串解析成Go 语言 cookie 结构体。...

Golang Range 的性能提升Tip

Go 语言里使用 range 可以方便遍历数组(array)、切片(slice)、字典(map)和信道(chan)。这里主要关注他们的性能。...

Golang 数据库 Bolt 碎片整理

Bolt 是一个优秀、纯 Go 实现、支持 ACID 事务的嵌入式 Key/Value 数据库。但在使用过程中会有很多空间碎片。一般数据库占用的空间是元数据空间的 1.5~4 倍。Bolt 没有内置的压缩功能,需要手动压缩。...

Write a Comment to "Golang 程序优雅关闭/grace shutdown 的实际应用"

Submit Comment Login
Based on Golang + fastHTTP + sdb | go1.22.3 Processed in 0ms