优雅地关闭 Gorilla 服务器

Gracefully Shutdown Gorilla Server

我正在使用 https://github.com/gorilla/mux 中的大猩猩多路复用器库构建一个服务器。 问题是,我希望它在我使用 Ctrl+C 时正常关闭,或者在有特定的 API 调用时正常关闭,例如“/shutdown”。

我已经知道Go 1.8已经实现了优雅关机。但是如何将它与大猩猩多路复用器结合起来呢?另外,如何结合SIGINT信号?

谁能告诉我怎么做?

通道可用于通过API调用(/shutdown)或中断信号(Ctrl+C)捕获关闭请求。

  1. http.Server 嵌入到自定义结构中,以便稍后调用 http Server.Shutdown
  2. 添加通道字段 (shutdownReq) 以传递来自 API 调用 /shutdown
  3. 的关闭请求
  4. gorilla/mux 的路由器中注册包括 /shutdown 的 http 处理程序,然后将路由器分配给 http.Server.Handler
  5. 注册os.Interrupt/syscall.SIGINT, syscall.SIGTERM处理程序
  6. 使用select通过API调用或interrupt信号捕获关机请求
  7. 通过调用 Server.Shutdown
  8. 执行干净关机

示例代码如下:

package main

import (
    "context"
    "log"
    "net/http"
    "sync/atomic"
    "syscall"
    "time"

    "os"
    "os/signal"

    "github.com/gorilla/mux"
)

type myServer struct {
    http.Server
    shutdownReq chan bool
    reqCount    uint32
}

func NewServer() *myServer {
    //create server
    s := &myServer{
        Server: http.Server{
            Addr:         ":8080",
            ReadTimeout:  10 * time.Second,
            WriteTimeout: 10 * time.Second,
        },
        shutdownReq: make(chan bool),
    }

    router := mux.NewRouter()

    //register handlers
    router.HandleFunc("/", s.RootHandler)
    router.HandleFunc("/shutdown", s.ShutdownHandler)

    //set http server handler
    s.Handler = router

    return s
}

func (s *myServer) WaitShutdown() {
    irqSig := make(chan os.Signal, 1)
    signal.Notify(irqSig, syscall.SIGINT, syscall.SIGTERM)

    //Wait interrupt or shutdown request through /shutdown
    select {
    case sig := <-irqSig:
        log.Printf("Shutdown request (signal: %v)", sig)
    case sig := <-s.shutdownReq:
        log.Printf("Shutdown request (/shutdown %v)", sig)
    }

    log.Printf("Stoping http server ...")

    //Create shutdown context with 10 second timeout
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()

    //shutdown the server
    err := s.Shutdown(ctx)
    if err != nil {
        log.Printf("Shutdown request error: %v", err)
    }
}

func (s *myServer) RootHandler(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello Gorilla MUX!\n"))
}

func (s *myServer) ShutdownHandler(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Shutdown server"))

    //Do nothing if shutdown request already issued
    //if s.reqCount == 0 then set to 1, return true otherwise false
    if !atomic.CompareAndSwapUint32(&s.reqCount, 0, 1) {
        log.Printf("Shutdown through API call in progress...")
        return
    }

    go func() {
        s.shutdownReq <- true
    }()
}

func main() {
    //Start the server
    server := NewServer()

    done := make(chan bool)
    go func() {
        err := server.ListenAndServe()
        if err != nil {
            log.Printf("Listen and serve: %v", err)
        }
        done <- true
    }()

    //wait shutdown
    server.WaitShutdown()

    <-done
    log.Printf("DONE!")
}

注意:请观看this issue与正常关机相关的内容。