我的编程空间,编程开发者的网络收藏夹
学习永远不晚

详解Golang如何优雅的终止一个服务

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

详解Golang如何优雅的终止一个服务

前言

采用常规方式启动一个 Golang http 服务时,若服务被意外终止或中断,即未等待服务对现有请求连接处理并正常返回且亦未对服务停止前作一些必要的处理工作,这样即会造成服务硬终止。这种方式不是很优雅。

参看如下代码,该 http 服务请求路径为根路径,请求该路径,其会在 2s 后返回 hello。

var addr = flag.String("server addr", ":8080", "server address")

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        time.Sleep(2 * time.Second)
        fmt.Fprintln(w, "hello")
    })
    http.ListenAndServe(*addr, nil)
}

若服务启动后,请求http://localhost:8080/,然后使用 Ctrl+C 立即中断服务,服务即会立即退出(exit status 2),请求未正常返回(ERR_CONNECTION_REFUSED),连接即马上断了。

接下来介绍使用 http.Server 的 Shutdown 方法结合 signal.Notify 来优雅的终止服务。

1 Shutdown 方法

Golang http.Server 结构体有一个终止服务的方法 Shutdown,其 go doc 如下。

func (srv *Server) Shutdown(ctx context.Context) error
    Shutdown gracefully shuts down the server without interrupting any active
    connections. Shutdown works by first closing all open listeners, then
    closing all idle connections, and then waiting indefinitely for connections
    to return to idle and then shut down. If the provided context expires before
    the shutdown is complete, Shutdown returns the context's error, otherwise it
    returns any error returned from closing the Server's underlying Listener(s).

    When Shutdown is called, Serve, ListenAndServe, and ListenAndServeTLS
    immediately return ErrServerClosed. Make sure the program doesn't exit and
    waits instead for Shutdown to return.

    Shutdown does not attempt to close nor wait for hijacked connections such as
    WebSockets. The caller of Shutdown should separately notify such long-lived
    connections of shutdown and wait for them to close, if desired. See
    RegisterOnShutdown for a way to register shutdown notification functions.

    Once Shutdown has been called on a server, it may not be reused; future
    calls to methods such as Serve will return ErrServerClosed.

由文档可知:

使用 Shutdown 可以优雅的终止服务,其不会中断活跃连接。

其工作过程为:首先关闭所有开启的监听器,然后关闭所有闲置连接,最后等待活跃的连接均闲置了才终止服务。

若传入的 context 在服务完成终止前已超时,则 Shutdown 方法返回 context 的错误,否则返回任何由关闭服务监听器所引起的错误。

当 Shutdown 方法被调用时,Serve、ListenAndServe 及 ListenAndServeTLS 方法会立刻返回 ErrServerClosed 错误。请确保 Shutdown 未返回时,勿退出程序。

对诸如 WebSocket 等的长连接,Shutdown 不会尝试关闭也不会等待这些连接。若需要,需调用者分开额外处理(诸如通知诸长连接或等待它们关闭,使用 RegisterOnShutdown 注册终止通知函数)。

一旦对 server 调用了 Shutdown,其即不可再使用了(会报 ErrServerClosed 错误)。

有了 Shutdown 方法,我们知道在服务终止前,调用该方法即可等待活跃连接正常返回,然后优雅的关闭。

但服务启动后的某一时刻,程序如何知道服务被中断了呢?服务被中断时如何通知程序,然后调用 Shutdown 作处理呢?接下来看一下系统信号通知函数的作用。

2 signal.Notify 函数

signal 包的 Notify 函数提供系统信号通知的能力,其 go doc 如下。

func Notify(c chan<- os.Signal, sig ...os.Signal)
    Notify causes package signal to relay incoming signals to c. If no signals
    are provided, all incoming signals will be relayed to c. Otherwise, just the
    provided signals will.

    Package signal will not block sending to c: the caller must ensure that c
    has sufficient buffer space to keep up with the expected signal rate. For a
    channel used for notification of just one signal value, a buffer of size 1
    is sufficient.

    It is allowed to call Notify multiple times with the same channel: each call
    expands the set of signals sent to that channel. The only way to remove
    signals from the set is to call Stop.

    It is allowed to call Notify multiple times with different channels and the
    same signals: each channel receives copies of incoming signals
    independently.

由文档可知:

参数 c 是调用者的信号接收通道,Notify 可将进入的信号转到 c。sig 参数为需要转发的信号类型,若不指定,所有进入的信号都将会转到 c。

信号不会阻塞式的发给 c:调用者需确保 c 有足够的缓冲空间,以应对指定信号的高频发送。对于用于通知仅一个信号值的通道,缓冲大小为 1 即可。

同一个通道可以调用 Notify 多次:每个调用扩展了发送至该通道的信号集合。仅可调用 Stop 来从信号集合移除信号。

允许不同的通道使用同样的信号参数调用 Notify 多次:每个通道独立的接收进入信号的副本。

综上,有了 signal.Notify,传入一个 chan 并指定中断参数,这样当系统中断时,即可接收到信号。

参看如下代码,当使用 Ctrl+C 时,c 会接收到中断信号,程序会在打印“program interrupted”语句后退出。

func main() {
    c := make(chan os.Signal)
    signal.Notify(c, os.Interrupt)
    <-c
    log.Fatal("program interrupted")
}
$ go run main.go

Ctrl+C

2019/06/11 17:59:11 program interrupted
exit status 1

3 Server 优雅的终止

接下来我们使用如上 signal.Notify 结合 http.Server 的 Shutdown 方法实现服务优雅的终止。

如下代码,Handler 与文章开始时的处理逻辑一样,其会在2s后返回 hello。

创建一个 http.Server 实例,指定端口与 Handler。

声明一个 processed chan,其用来保证服务优雅的终止后再退出主 goroutine。

新启一个 goroutine,其会监听 os.Interrupt 信号,一旦服务被中断即调用服务的 Shutdown 方法,确保活跃连接的正常返回(本代码使用的 Context 超时时间为 3s,大于服务 Handler 的处理时间,所以不会超时)。

处理完成后,关闭 processed 通道,最后主 goroutine 退出。

代码同时托管在 GitHub,欢迎关注(github.com/olzhy/go-excercises)。

var addr = flag.String("server addr", ":8080", "server address")

func main() {
    // handler
    handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        time.Sleep(2 * time.Second)
        fmt.Fprintln(w, "hello")
    })

    // server
    srv := http.Server{
        Addr:    *addr,
        Handler: handler,
    }

    // make sure idle connections returned
    processed := make(chan struct{})
    go func() {
        c := make(chan os.Signal, 1)
        signal.Notify(c, os.Interrupt)
        <-c

        ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
        defer cancel()
        if err := srv.Shutdown(ctx); nil != err {
            log.Fatalf("server shutdown failed, err: %v\n", err)
        }
        log.Println("server gracefully shutdown")

        close(processed)
    }()

    // serve
    err := srv.ListenAndServe()
    if http.ErrServerClosed != err {
        log.Fatalf("server not gracefully shutdown, err :%v\n", err)
    }

    // waiting for goroutine above processed
    <-processed
}

总结

到此这篇关于Golang如何优雅的终止一个服务的文章就介绍到这了,更多相关Golang终止服务内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

详解Golang如何优雅的终止一个服务

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Golang怎么优雅的终止一个服务

今天小编给大家分享一下Golang怎么优雅的终止一个服务的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。前言采用常规方式启动一
2023-06-29

详解C#如何优雅地终止线程

在大多情况下,我们只关心线程的创建与启动,运行,却并不关心线程的结束或者终止。今天这篇文章,我们就以一些简单的小例子,简述如何有效的停止线程,仅供学习分享使用,如有不足之处,还请指正
2023-03-21

详解如何优雅在webpack项目实现mock服务器

这篇文章主要为大家介绍了详解如何优雅在webpack项目实现mock服务器,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-02-06

golang如何实现一个restful微服务的操作

这篇文章将为大家详细讲解有关golang如何实现一个restful微服务的操作,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。什么是golanggolang 是Google开发的一种静态强类型、编译型、并发
2023-06-14

如何在阿里云服务器中执行任务一个详细的指南

阿里云服务器是一款强大的云计算服务,它提供了一种高效的方式,让您可以在云服务器上执行任务。无论您是个人用户还是企业用户,阿里云服务器都能满足您的需求。然而,对于新手来说,如何在阿里云服务器中执行任务可能是一个挑战。本文将为您提供一个详细的指南,帮助您了解如何在阿里云服务器中执行任务。步骤一:了解阿里云服务器的基本
如何在阿里云服务器中执行任务一个详细的指南
2023-10-29

如何将阿里云服务器放在本地一个详细的步骤指南

如果你是一名程序员或者对云计算有浓厚兴趣,你可能会对将阿里云服务器放在本地进行开发和测试感兴趣。本文将详细介绍如何将阿里云服务器放在本地,包括所需的工具、步骤和注意事项。步骤:首先,你需要在阿里云上购买一个服务器实例。选择一个适合你的需求的实例类型,例如Linux服务器或者Windows服务器。下一步,你需要将服
如何将阿里云服务器放在本地一个详细的步骤指南
2023-12-10

阿里云的ecs服务器可以降低配置吗?详解如何优化资源配置

在当前信息化时代,云计算作为一种高效、灵活的计算方式已经深入到各行各业。特别是阿里云的ECS服务器,更是因其强大的性能和稳定性,得到了广大用户的认可和好评。然而,对于很多用户来说,如何有效地利用ECS服务器资源,降低配置是一个亟待解决的问题。本文将详细解释如何优化资源配置,以降低阿里云的ECS服务器配置。正文:阿
阿里云的ecs服务器可以降低配置吗?详解如何优化资源配置
2023-11-15

如何解决笔记本电脑开机显示未能连接一个windows服务的问题

小编给大家分享一下如何解决笔记本电脑开机显示未能连接一个windows服务的问题,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!1.调出运行窗口,开始在界面中输入c
2023-06-27

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录