脚本之家,脚本语言编程技术及教程分享平台!
分类导航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|shell|

服务器之家 - 脚本之家 - Golang - go如何优雅关闭Graceful Shutdown服务

go如何优雅关闭Graceful Shutdown服务

2023-05-16 15:20cainmusic Golang

这篇文章主要为大家介绍了go优雅关闭Graceful Shutdown服务详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

Shutdown方法

Go1.8之后有了Shutdown方法,用来优雅的关闭(Graceful Shutdown)服务。

?
1
func (srv *Server) Shutdown(ctx context.Context) error

这个方法会在调用时进行下述操作:

1.关闭所有open listeners

2.关闭所有idle connections

3.无限期等待connections回归idle状态

4.之后关闭服务

注:3的无限期等待可以用context的超时来解决。

RegisterOnShutdown方法

Go1.9之后有了RegisterOnShutdown方法,用来在优雅的关闭服务的同时处理一些退出任务。

?
1
func (srv *Server) RegisterOnShutdown(f func())

关于RegisterOnShutdown如何生效,我们看代码。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
type Server struct {
    ...
    onShutdown []func()
}
 
func (srv *Server) RegisterOnShutdown(f func()) {
    srv.mu.Lock()
    srv.onShutdown = append(srv.onShutdown, f)
    srv.mu.Unlock()
}
 
func (srv *Server) Shutdown(ctx context.Context) error {
    ...
 
    srv.mu.Lock()
    ...
    for _, f := range srv.onShutdown {
        go f()
    }
    srv.mu.Unlock()
 
    ...
}

由代码可知,在通过RegisterOnShutdown进行注册之后,这些函数被放入一个函数切片中,并在Shutdown被调用的时候,为每个函数启动了一个goroutine进行处理。

但由于没有进行后续处理,于是这里有个小问题,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
package main
import (
    "context"
    "log"
    "net/http"
    "os"
    "os/signal"
    "time"
)
func main() {
    var srv http.Server
    srv.RegisterOnShutdown(func() {
        time.Sleep(3 * time.Second)
        log.Println("Exit func")
    })
    idleConnsClosed := make(chan struct{})
    go func() {
        sigint := make(chan os.Signal, 1)
        signal.Notify(sigint, os.Interrupt)
        <-sigint
        // We received an interrupt signal, shut down.
        if err := srv.Shutdown(context.Background()); err != nil {
            // Error from closing listeners, or context timeout:
            log.Printf("HTTP server Shutdown: %v", err)
        }
        close(idleConnsClosed)
    }()
    if err := srv.ListenAndServe(); err != http.ErrServerClosed {
        // Error starting or closing listener:
        log.Fatalf("HTTP server ListenAndServe: %v", err)
    }
    <-idleConnsClosed
}

运行时Ctrl+c之后,服务立刻结束了,并未等待log.Println("Exit func")的输出。

sync.WaitGroup处理退出函数

为了解决这个问题我们使用sync.WaitGroup来处理这些退出函数,于是我们有了下面的代码(解耦并增加了一些日志):

?
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
package main
import (
    "context"
    "log"
    "net/http"
    "os"
    "os/signal"
    "sync"
    "time"
)
func main() {
    var srv http.Server
    var wg sync.WaitGroup
    register := func(f func()) {
        wg.Add(1)
        log.Println("Exit func register")
        srv.RegisterOnShutdown(func() {
            defer wg.Done()
            f()
            log.Println("Exit func done")
        })
    }
    register(func() {
        time.Sleep(3 * time.Second)
        log.Println("Called on Shutdown")
    })
    idleConnsClosed := make(chan struct{})
    go func() {
        sigint := make(chan os.Signal, 1)
        signal.Notify(sigint, os.Interrupt)
        <-sigint
        // We received an interrupt signal, shut down.
        log.Println("Shutdown start")
        if err := srv.Shutdown(context.Background()); err != nil {
            // Error from closing listeners, or context timeout:
            log.Printf("HTTP server Shutdown: %v", err)
        }
        close(idleConnsClosed)
    }()
    if err := srv.ListenAndServe(); err != http.ErrServerClosed {
        // Error starting or closing listener:
        log.Fatalf("HTTP server ListenAndServe: %v", err)
    }
    <-idleConnsClosed
    log.Println("Shutdown finish")
    wg.Wait()
}

我们在RegisterOnShutdown时使用了time.Sleep(3 * time.Second)来模拟一个长时间的退出程序,并且在"Shutdown finish"之后进行等待,等待退出程序完成任务。

执行日志如下:

2023/05/12 16:48:53 Exit func register
^C2023/05/12 16:48:54 Shutdown start
2023/05/12 16:48:54 Shutdown finish
2023/05/12 16:48:57 Called on Shutdown
2023/05/12 16:48:57 Exit func done

可以看到这个服务本身很轻,Shutdown start之后立刻就finish并来到wg.Wait(),并等待register的函数完成之后再退出程序。

另外,如果在服务运行的过程中需要启动一些其他的任务,也可以使用

?
1
2
3
4
5
wg.Add(1)
go func() {
    defer wg.Done()
    ...
}

来保证任务在服务Shutdown之后可以顺利完成。

以上就是go如何优雅关闭Graceful Shutdown服务的详细内容,更多关于go关闭Graceful Shutdown的资料请关注服务器之家其它相关文章!

原文链接:https://segmentfault.com/a/1190000043782350

延伸 · 阅读

精彩推荐
  • Golang写 Go 时如何优雅地查文档

    写 Go 时如何优雅地查文档

    Goland 的自动补全功能已经是必需品了,只要打出相关的几个字符,不管是变量名还是函数调用,都能帮你直接补全。我们只需要往相应的位置填东西就行了...

    码农桃花源10652021-09-08
  • Golanggo select编译期的优化处理逻辑使用场景分析

    go select编译期的优化处理逻辑使用场景分析

    select 是 Go 中的一个控制结构,类似于用于通信的 switch 语句。每个 case 必须是一个通信操作,要么是发送要么是接收。接下来通过本文给大家介绍go selec...

    yuchenfw8982021-08-11
  • Golanggolang 监听服务的信号,实现平滑启动,linux信号说明详解

    golang 监听服务的信号,实现平滑启动,linux信号说明详解

    这篇文章主要介绍了golang 监听服务的信号,实现平滑启动,linux信号说明详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    胖达喵12072021-06-23
  • GolangGo GORM版本2.0新特性介绍

    Go GORM版本2.0新特性介绍

    这篇文章主要为大家介绍了Go GORM版本2.0新特性的使用示例介绍,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪...

    王中阳Go8952022-10-17
  • GolangGolang操作命令行的几种方式总结

    Golang操作命令行的几种方式总结

    这篇文章主要介绍了Golang操作命令行的几种方式总结,文章通过围主题思想展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下...

    jiaxwu​​​​​​​10412022-11-17
  • GolangGo和Scala等编程语言的比较研究

    Go和Scala等编程语言的比较研究

    编程语言对于追求成功的数据科学事业至关重要。在此处了解有关流行语言-Go和Scala的更多信息。...

    今日头条11032020-12-29
  • Golanggolang实现简单rpc调用过程解析

    golang实现简单rpc调用过程解析

    这篇文章主要介绍了golang实现简单rpc调用,包括RPC具体实现结合实例代码给大家讲解的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友...

    神技圈子4522022-10-07
  • Golanggo实现冒泡排序算法

    go实现冒泡排序算法

    冒泡排序算法是数据结构中常用的一种算法,本文就介绍了go实现冒泡排序算法,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙...

    嵌入式职场8452022-09-06