专业的编程技术博客社区

网站首页 > 博客文章 正文

Go中如何优雅关闭HttpServer?(关闭go module)

baijin 2025-03-30 14:17:07 博客文章 9 ℃ 0 评论

一、前言

go-SDK内提供了net模块,让我们可以方便的搭建http服务,我们知道服务一旦启动,调用协程阻塞就等待了,然后等待接受客户端发来的请求。那么当我们想要停止服务时,如何优雅的关闭已经启动的服务那?

二、优雅停服



func sayHello(w http.ResponseWriter, r *http.Request) {
  fmt.Println("path", r.URL.Path)
  fmt.Fprintln(w, "hello world")
}


func main() {


  //1. 创建http请求处理器,并注册path与处理器
  handler := http.NewServeMux()
  handler.HandleFunc("/hello", sayHello)


  //2. 创建httpserver
  server := &http.Server{Addr: ":9090", Handler: handler}
  // 用于实现通知等待模型
  shutdownFlag := make(chan int)


  //3. 异步协程,模拟优雅停服
  go func() {
    //3.1 休眠1分钟,让服务器有1分钟时间可以处理请求
    time.Sleep(time.Minute)
    fmt.Println("call shutdown")
    //3.2如果1分钟还没关闭完毕,则报错
    ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
    defer cancel()
    //3.3优雅关闭
    err1 := server.Shutdown(ctx)
    if err1 != nil {
      fmt.Printfln(err1)


    }


    //3.3通知main协程,关闭完毕,可以退出了
    close(shutdownFlag)
  }()


  //4. 启动http服务
  err := server.ListenAndServe()
  if err != http.ErrServerClosed {
    panic(err)
  }


  //5. 等待优雅关闭结束
  <-shutdownFlag
  fmt.Println("shutdown ok ")
}
  • 代码1 注册path与处理器,代码2创建httpserver,在端口9090监听服务请求
  • 代码4 启动http服务,然后main函数所在协程就会阻塞了。
  • 代码3 异步协程模拟优雅停服。这里先休眠1分钟,是为了在1分钟让服务器可以正常接受请求处理。1分钟后,调用了server的shutdown方法,执行优雅停服,停服成功后,代码4会返回ErrServerClosed错误。

三、shutdown内部逻辑

  • 首先关闭所有开启的监听器,然后关闭所有闲置连接,最后等待活跃的连接均闲置了才终止服务。
  • 如果传入的 context 在服务完成终止前已超时了,那么 Shutdown 方法返回 context 的错误,否则返回任何由关闭服务监听器所引起的错误。
  • 另外当 Shutdown 方法被调用时,Serve、ListenAndServe 及 ListenAndServeTLS 方法会立刻返回 ErrServerClosed 错误。所以需要确保 Shutdown 未返回时,main函数所在的协程不要退出。我们示例中代码代码3.3和代码5保证了这个
  • 对 WebSocket 等的长连接,Shutdown 不会尝试关闭也不会等待这些连接关闭。若需要关闭,需调用者分开额外处理(诸如通知诸长连接或等待它们关闭,使用 RegisterOnShutdown 注册终止通知函数)。
  • 一旦对 server 调用了 Shutdown,其就不可再使用了(会报 ErrServerClosed 错误)。

四、总结

使用shutdown方法可以优雅停服,但是需要保证main函数所在协程不能在停服完毕前就退出了,因为go中main函数所在协程退出意味着整个进程就退出了,而这时优雅停服可能还没完成。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表