【golang】go app 优雅关机 Graceful Shutdown How?
创始人
2024-03-30 11:31:59

 

0 背景     

        我们使用 Go (Golang)来构建一个非常简单的 API 服务器,它可以优雅地关闭。优雅的关闭是指所有进程都被正确关闭,当前正在运行的任何 API 请求都不会被中断,并且将首先完成运行,最后拒绝任何新的 API 请求。

       在本文中,我们将学习构建一个简单的 API 服务器,并了解如何截获外部关机信号,以便通知服务器我们正在关闭。

1 直接关停有什么问题?

  • 服务器立即关闭,go程序不会捕捉到错误!
  • 如果请求正在处理,它将突然停止。正在处理的请求不会得到相应。下面例子中可以通过添加时间来验证这一点,睡眠(10秒 在请求处理程序中延长请求持续时间)。

2. 不优雅关机版本

  • (1)ーー声明一个简单的路由并分配一个处理程序
  • (2)ーー启动服务器(程序将在这里停止,而服务器接受请求)
  • (3)ーー处理服务器退出时停止接受请求的错误)
  • (4)ーー处理请求的简单路由处理程序
package mainimport ("fmt""net/http"
)func main() {fmt.Println("Hello, NO Graceful Server!")// (1) api route and handlerhttp.HandleFunc("/hello", HelloAPI_)// (2) start the servererr := http.ListenAndServe(":8080", nil)// (3) handle error on exitif err != nil {panic(err)}
}// (4) A simple basic implementation of handling an API request
func HelloAPI_(w http.ResponseWriter, r *http.Request) {time.Sleep(10 * time.Second)response := "Hello!"w.Write([]byte(response))
}

3. 优雅关机版

  • (1)首先我们需要创建一个多路复用器(简称 mux) ,它基本上是一个请求处理程序
  • (2)使用 mux,我们注册路由和处理程序。这与我们之前使用 http 包所做的几乎相同
  • (3)然后创建服务器。对于这个对象,稍后我们将了解关闭方法
  • (4)然后我们通过调用... ListenAndServe
  • (5)来启动服务器。如果服务器无法启动,我们可能应该记录错误
  • (6)我们需要检查错误是否是由优雅的关闭引起的,并且安全地忽略它
  • (7)我们需要以某种方式调用 Shutdown 方法来优雅地关闭服务器并且拒绝正确的请求,等待任何处理请求完成。
  • (8)最后,如果不能正确执行关机,我们也会记录错误
package mainimport ("context""fmt""net/http""os""os/signal""syscall""time"
)func main() {// print that the application has startedfmt.Println("Hello, Graceful Server!")// create a server muxserverMux := http.NewServeMux()// register route and handlerserverMux.HandleFunc("/hello", HelloAPI__)// create serverserver := &http.Server{Addr:    ":8080",Handler: serverMux,}// create channel to capture signalsigChn := make(chan os.Signal, 1)// register channel for signal capturing// 注册监听系统信号量signal.Notify(sigChn, syscall.SIGINT)// create channel to capture server errorstartErrChn := make(chan error, 1)// start the server asynchronouslygo func() {// (4) start the servererr := server.ListenAndServe()// (5) handle error on exitif err != nil {if err == http.ErrServerClosed {// do nothing...} else {// log errorfmt.Println(err)}}// inform that server has stopped accepting requestsstartErrChn <- err}()// wait for either a Ctrl+C signal, or server abnormal start errorselect {// we captured a signal to shut down applicationcase <-sigChn:// print that server is shutting downfmt.Println("server is shutting down")// trigger the server shutdown gracefullyerr := server.Shutdown(context.Background())// log any error on graceful exitif err != nil {fmt.Println(err)}// we have an error from server's listen and serve, which is abnormal shutdowncase <-startErrChn:// since we already logged the error, we may want to log additional detailsfmt.Println("server abnormal shutdown without stop signal!")}// tidy up and print that we have gracefully shutdown the serverfmt.Println("Graceful shutdown!")
}// a long running request
func HelloAPI__(w http.ResponseWriter, r *http.Request) {time.Sleep(150 * time.Second)response := "Hello!"w.Write([]byte(response))
}

// 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.
func (srv *Server) Shutdown(ctx context.Context) error {srv.inShutdown.setTrue()srv.mu.Lock()lnerr := srv.closeListenersLocked()srv.closeDoneChanLocked()for _, f := range srv.onShutdown {go f()}srv.mu.Unlock()pollIntervalBase := time.MillisecondnextPollInterval := func() time.Duration {// Add 10% jitter.interval := pollIntervalBase + time.Duration(rand.Intn(int(pollIntervalBase/10)))// Double and clamp for next time.pollIntervalBase *= 2if pollIntervalBase > shutdownPollIntervalMax {pollIntervalBase = shutdownPollIntervalMax}return interval}timer := time.NewTimer(nextPollInterval())defer timer.Stop()for {if srv.closeIdleConns() && srv.numListeners() == 0 {return lnerr}select {case <-ctx.Done():return ctx.Err()case <-timer.C:timer.Reset(nextPollInterval())}}
}

 

 4. 小结&问题

  • 1 上面代码如果ctr+c捕获系统信号之后的处理都能解决:「当前正在运行的任何 API 请求都不会被中断」,但是如果代码主动关停确实不行的,为什么呢?如下改动(HelloAPI_最终没有执行完)
  •  2. 核心方案(拦截信号):我们让 Go 系统在信号被拦截时通知我们,使用channel实现;

 

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...