优雅地关闭大猩猩服务器

时间:2017-04-26 10:39:47

标签: go webserver channel sigint gorilla

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

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

任何人都可以告诉我该怎么做吗?

1 个答案:

答案 0 :(得分:6)

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

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

    以下是示例代码:

    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!")
    }
    

    注意:请注意与gracefull关闭相关的this issue