乐趣区

关于golang:golang-nethttp的优雅关机

什么是优雅关机

http-server 运行过程中,若过程被敞开,那么正在解决的申请可能只被解决了一半就进行了,可能会产生数据的不统一。
优雅关机是指:

  • 首先,进行接管新申请;
  • 而后,期待队列中的申请被处理完毕;
  • 最初,应用程序退出;

net/http 如何实现优雅关机

net/http 原生反对优雅关机。

首先,在 goroutine 中启动 http-server:

srv := &http.Server{
  Addr:              ":8090",
  Handler:           r,
}
go func() {if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {log.Fatal("listen:", err)
  } else {log.Println("ListenAndServe break")
  }
}()

而后,在 main 中监听关机信号:

  • SIGTERM: kill 的默认信号;
  • SIGINT:kill -2,个别是 Ctrl+ C 的退出;
  • SIGKILL:kill -9,捕捉不到;
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<- quit

最初,监听到关机信号后,执行优雅关机:

  • 调用 srv.Shutdown() 执行优雅关机;
  • 默认期待所有队列中的申请处理完毕后,才返回;
  • 传入 timeoutContext,减少超时工夫,超时工夫到后返回;
ctx, cancel := context.WithTimeout(context.TODO(), 20*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {log.Fatal("Server shutdown:", err)
}

net/http 优雅关机的实现原理

优雅关机是调用 net/http 的 srv.Shutdown(ctx) 实现的,该办法会:

  • 先回绝前面的 connection 申请;
  • 而后再缓缓的解决未处理完毕的申请;
  • 当未解决的申请一旦被处理完毕,其 connection 变成 Idle,而后被 Close() 掉;
func (srv *Server) Shutdown(ctx context.Context) error {
    ...
    for _, f := range srv.onShutdown {go f()
    }
    ...

    ticker := time.NewTicker(shutdownPollInterval)
    defer ticker.Stop()
    for {if srv.closeIdleConns() {    // 敞开 idle 连贯
          return lnerr
       }
       select {case <-ctx.Done():       //ctx 到期,如 cancel() 被调用
              return ctx.Err()
           case <-ticker.C:
       }
    }
}

另外,当调用 Shutdown() 后,srv.ListenAndServer 办法将退出,并返回 ErrServerClose 谬误。

退出移动版