您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > Go语言

Golang 优雅的终止一个服务

时间:2019-08-29 09:45:04  来源:  作者:
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方法,我们知道在服务终止前,调用该方法即可等待活跃连接正常返回,然后优雅的关闭。

关于上面用到的Golang Context参数,之前专门写过一篇文章介绍了Context的使用场景(请参考:Context 你使用过了吧?本文和你一起总结 Golang Context 的使用)。

但服务启动后的某一时刻,程序如何知道服务被中断了呢?服务被中断时如何通知程序,然后调用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: %vn", err)
 }
 log.Println("server gracefully shutdown")
 close(processed)
 }()
 // serve
 err := srv.ListenAndServe()
 if http.ErrServerClosed != err {
 log.Fatalf("server not gracefully shutdown, err :%vn", err)
 }
 // waiting for goroutine above processed
 <-processed
}

原文链接:https://leileiluoluo.com/posts/golang-shutdown-server-gracefully.html

本文作者:磊磊落落的博客,原创授权发布



Tags:Golang   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
golang context 很好用,就使用php实现了github地址 : https://github.com/qq1060656096/php-go-context context使用闭坑指南1. 将一个Context参数作为第一个参数传递给传入和...【详细内容】
2021-11-05  Tags: Golang  点击:(41)  评论:(0)  加入收藏
简介工作中经常有定时执行某些代码块的需求,如果是PHP代码,一般写个脚本,然后用Cron实现。Go里提供了两种定时器:Timer(到达指定时间触发且只触发一次)和 Ticker(间隔特定时间触发)...【详细内容】
2021-05-10  Tags: Golang  点击:(310)  评论:(0)  加入收藏
不管你学没学过golang,都不妨碍这个21世纪的c语言正变得越来越流行,越来越多的平台服务使用golang来构建,我们熟知的docker就是采用golang语言进行开发设计的。谷歌作为golang...【详细内容】
2021-05-10  Tags: Golang  点击:(505)  评论:(0)  加入收藏
之前用 go 写一个小工具的时候, 用到了多个协程之间的通信, 当时随手查了查, 结果查出来一大坨, 简单记录一下. golang中多个协程之间是如何进行通信及数据同步的嘞.共享变...【详细内容】
2021-02-25  Tags: Golang  点击:(423)  评论:(0)  加入收藏
12月初,我们发现了一种新的用Golang编写的蠕虫。该蠕虫延续了 Golang在2020年流行的多平台恶意软件趋势。...【详细内容】
2021-01-05  Tags: Golang  点击:(175)  评论:(0)  加入收藏
本文主要研究一下golang的zap的ReflectType sweetenFieldszap@v1.16.0/sugar.gofunc (s *SugaredLogger) sweetenFields(args []interface{}) []Field { if len(args) ==...【详细内容】
2020-12-22  Tags: Golang  点击:(64)  评论:(0)  加入收藏
Golang的匿名结构是什么?匿名结构就像普通结构一样,但是它没有名称定义,因此不能在代码的其他地方引用。Go中的结构类似于C等其他语言中的结构。它们是字段的类型化集合,用于将...【详细内容】
2020-12-17  Tags: Golang  点击:(140)  评论:(0)  加入收藏
前文《理解 Paxos》只包含伪代码,帮助了理解但又不够爽,既然现在都讲究 Talk is cheap. Show me the code.这次就把文章中的伪代码用 Go 语言实现出来,希望能帮助各位朋友更直...【详细内容】
2020-12-15  Tags: Golang  点击:(116)  评论:(0)  加入收藏
今天逛github超市时,发现一个非常不错的数据可视化库go-echarts,特分享给大家。介绍在 Golang 这门语言中,目前数据可视化的第三方库还是特别少,go-echarts的开发就是为了填补这...【详细内容】
2020-12-15  Tags: Golang  点击:(88)  评论:(0)  加入收藏
由于Golang的语言设计的原因,不管是不是愿意,每个golang开发者的几乎每一段代码都需要与error做缠斗。下面我就简单分析一下golang中的error相关。...【详细内容】
2020-12-02  Tags: Golang  点击:(86)  评论:(0)  加入收藏
▌简易百科推荐
zip 是一种常见的归档格式,本文讲解 Go 如何操作 zip。首先看看 zip 文件是如何工作的。以一个小文件为例:(类 Unix 系统下)$ cat hello.textHello!执行 zip 命令进行归档:$ zip...【详细内容】
2021-12-17  Go语言中文网    Tags:Go语言   点击:(13)  评论:(0)  加入收藏
大家好,我是 polarisxu。前段时间,Russ Cox 明确了泛型相关的事情,原计划在标准库中加入泛型相关的包,改放到 golang.org/x/exp 下。目前,Go 泛型的主要设计者 ianlancetaylor 完...【详细内容】
2021-11-30  Go语言中文网    Tags:slices 包   点击:(24)  评论:(0)  加入收藏
前言最近因为项目需要写了一段时间的 Go ,相对于 Java 来说语法简单同时又有着一些 Python 之类的语法糖,让人大呼”真香“。 但现阶段相对来说还是 Python 写的多一些,偶尔还...【详细内容】
2021-11-25  crossoverJie    Tags:Go   点击:(29)  评论:(0)  加入收藏
go-micro是基于 Go 语言用于开发的微服务的 RPC 框架,主要功能如下:服务发现,负载均衡 ,消息编码,请求/响应,Async Messaging,可插拔接口,最后这个功能牛p安装步骤安装proto...【详细内容】
2021-09-06    石老师小跟班  Tags:go-micro   点击:(197)  评论:(0)  加入收藏
GoLand 2021.2 EAP 5 现已发布。用户可以从工具箱应用程序中获得 EAP 构建,也可以从官方网站手动下载。并且从此 EAP 开始,只有拥有有效的 JetBrains 帐户才能加入该计划。手...【详细内容】
2021-06-29  IT实战联盟  今日头条  Tags:GoLand   点击:(185)  评论:(0)  加入收藏
作者:HDT3213今天给大家带来的开源项目是 Godis:一个用 Go 语言实现的 Redis 服务器。支持: 5 种数据结构(string、list、hash、set、sortedset) 自动过期(TTL) 发布订阅、地理位...【详细内容】
2021-06-18  HelloGitHub  今日头条  Tags:Go   点击:(125)  评论:(0)  加入收藏
统一规范篇合理规划目录本篇主要描述了公司内部同事都必须遵守的一些开发规矩,如统一开发空间,既使用统一的开发工具来保证代码最后的格式的统一,开发中对文件和代码长度的控制...【详细内容】
2021-05-18  1024课堂    Tags:Go语言   点击:(232)  评论:(0)  加入收藏
闭包概述 闭包不是Go语言独有的概念,在很多编程语言中都有闭包 闭包就是解决局部变量不能被外部访问的一种解决方案 是把函数当作返回值的一种应用 代码演示总体思想:在函数...【详细内容】
2021-05-14  HelloGo  今日头条  Tags:Go语言   点击:(223)  评论:(0)  加入收藏
一时想不开,想了解一下Go语言,于是安装了并体验了一下。下载1. 进入golang.google.cn 点击Download Go 2.选择对应的操作系统,点击后开始下载。 安装1. windows下执行傻瓜式安...【详细内容】
2021-05-12  程序员fearlazy  fearlazy  Tags:Go语言   点击:(236)  评论:(0)  加入收藏
1.简介channel是Go语言的一大特性,基于channel有很多值得探讨的问题,如 channel为什么是并发安全的? 同步通道和异步通道有啥区别? 通道为何会阻塞协程? 使用通道导致阻塞的协程...【详细内容】
2021-05-10  程序员麻辣烫  今日头条  Tags:Go通道   点击:(274)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条