http.ListenAndServe () 중지 방법 서버와 함께 Gorilla Web Toolkit의 Mux 라이브러리를

번들 Go http 서버와 함께 Gorilla Web Toolkit의 Mux 라이브러리를 사용하고 있습니다.

문제는 내 응용 프로그램에서 HTTP 서버가 하나의 구성 요소 일 뿐이며 재량에 따라 중지하고 시작해야한다는 것입니다.

내가 http.ListenAndServe(fmt.Sprintf(":%d", service.Port()), service.router)그것을 블록 이라고 부르면 서버가 실행되는 것을 막을 수 없습니다.

나는 이것이 과거에 문제 였다는 것을 알고 있는데, 여전히 그럴까요? 새로운 솔루션이 있습니까?



답변

정상 종료 (Go 1.8에 도입 됨)와 관련하여 좀 더 구체적인 예 :

package main

import (
    "context"
    "io"
    "log"
    "net/http"
    "sync"
    "time"
)

func startHttpServer(wg *sync.WaitGroup) *http.Server {
    srv := &http.Server{Addr: ":8080"}

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        io.WriteString(w, "hello world\n")
    })

    go func() {
        defer wg.Done() // let main know we are done cleaning up

        // always returns error. ErrServerClosed on graceful close
        if err := srv.ListenAndServe(); err != http.ErrServerClosed {
            // unexpected error. port in use?
            log.Fatalf("ListenAndServe(): %v", err)
        }
    }()

    // returning reference so caller can call Shutdown()
    return srv
}

func main() {
    log.Printf("main: starting HTTP server")

    httpServerExitDone := &sync.WaitGroup{}

    httpServerExitDone.Add(1)
    srv := startHttpServer(httpServerExitDone)

    log.Printf("main: serving for 10 seconds")

    time.Sleep(10 * time.Second)

    log.Printf("main: stopping HTTP server")

    // now close the server gracefully ("shutdown")
    // timeout could be given with a proper context
    // (in real world you shouldn't use TODO()).
    if err := srv.Shutdown(context.TODO()); err != nil {
        panic(err) // failure/timeout shutting down the server gracefully
    }

    // wait for goroutine started in startHttpServer() to stop
    httpServerExitDone.Wait()

    log.Printf("main: done. exiting")
}


답변

yo.ian.g의 답변 에서 언급했듯이 . Go 1.8은이 기능을 표준 lib에 포함 시켰습니다.

에 대한 최소 예 Go 1.8+:

    server := &http.Server{Addr: ":8080", Handler: handler}

    go func() {
        if err := server.ListenAndServe(); err != nil {
            // handle err
        }
    }()

    // Setting up signal capturing
    stop := make(chan os.Signal, 1)
    signal.Notify(stop, os.Interrupt)

    // Waiting for SIGINT (pkill -2)
    <-stop

    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    if err := server.Shutdown(ctx); err != nil {
        // handle err
    }

    // Wait for ListenAndServe goroutine to close.

원래 답변-Pre Go 1.8 :

바탕 Uvelichitel의 대답.

ListenAndServe를 반환하고 io.Closer차단하지 않는 고유 한 버전을 만들 수 있습니다 .

func ListenAndServeWithClose(addr string, handler http.Handler) (io.Closer,error) {

    var (
        listener  net.Listener
        srvCloser io.Closer
        err       error
    )

    srv := &http.Server{Addr: addr, Handler: handler}

    if addr == "" {
        addr = ":http"
    }

    listener, err = net.Listen("tcp", addr)
    if err != nil {
        return nil, err
    }

    go func() {
        err := srv.Serve(tcpKeepAliveListener{listener.(*net.TCPListener)})
        if err != nil {
            log.Println("HTTP Server Error - ", err)
        }
    }()

    srvCloser = listener
    return srvCloser, nil
}

여기에서 전체 코드를 확인할 수 있습니다 .

HTTP 서버가 오류와 함께 닫힙니다.
accept tcp [::]:8080: use of closed network connection


답변

Go 1.8에는 Server::Shutdown(context.Context)Server::Close()각각을 통해 사용할 수있는 정상 및 강제 종료가 포함 됩니다.

go func() {
    httpError := srv.ListenAndServe(address, handler)
    if httpError != nil {
        log.Println("While serving HTTP: ", httpError)
    }
}()

srv.Shutdown(context)

관련 커밋은 여기 에서 찾을 수 있습니다 .


답변

당신은 건설 할 수 있습니다 net.Listener

l, err := net.Listen("tcp", fmt.Sprintf(":%d", service.Port()))
if err != nil {
    log.Fatal(err)
}

당신이 할 수있는 Close()

go func(){
    //...
    l.Close()
}()

그리고 http.Serve()그것에

http.Serve(l, service.router)


답변

이전 답변 중 http.ListenAndServe ()를 사용하면 왜 할 수 없는지 말하지 않았기 때문에 v1.8 http 소스 코드로 들어가서 다음과 같이 말합니다.

func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return server.ListenAndServe()
}

보시다시피 http.ListenAndServe 함수는 서버 변수를 반환하지 않습니다. 즉, Shutdown 명령을 사용하기 위해 ‘서버’에 액세스 할 수 없습니다. 따라서 정상적인 종료를 구현하려면이 기능을 사용하는 대신 고유 한 ‘서버’인스턴스를 만들어야합니다.


답변

컨텍스트를 닫아 서버를 닫을 수 있습니다.

type ServeReqs func(ctx context.Context, cfg Config, deps ReqHandlersDependencies) error

var ServeReqsImpl = func(ctx context.Context, cfg Config, deps ReqHandlersDependencies) error {
    http.Handle(pingRoute, decorateHttpRes(pingHandlerImpl(deps.pingRouteResponseMessage), addJsonHeader()))

    server := &http.Server{Addr: fmt.Sprintf(":%d", cfg.port), Handler: nil}

    go func() {
        <-ctx.Done()
        fmt.Println("Shutting down the HTTP server...")
        server.Shutdown(ctx)
    }()

    err := server.ListenAndServeTLS(
        cfg.certificatePemFilePath,
        cfg.certificatePemPrivKeyFilePath,
    )

    // Shutting down the server is not something bad ffs Go...
    if err == http.ErrServerClosed {
        return nil
    }

    return err
}

닫을 준비가되면 다음으로 전화하십시오.

ctx, closeServer := context.WithCancel(context.Background())
err := ServeReqs(ctx, etc)
closeServer()


답변

또한 이것은 사용 해결할 수있다 context.Context사용 a net.ListenConfig. 제 경우에는 sync.WaitGroup또는 http.ServerShutdown()호출 을 사용하고 싶지 않았고 대신 a context.Context(신호로 닫힘) 에 의존했습니다 .

import (
  "context"
  "http"
  "net"
  "net/http/pprof"
)

func myListen(ctx context.Context, cancel context.CancelFunc) error {
  lc := net.ListenConfig{}
  ln, err := lc.Listen(ctx, "tcp4", "127.0.0.1:6060")
  if err != nil {
    // wrap the err or log why the listen failed
    return err
  }

  mux := http.NewServeMux()
  mux.Handle("/debug/pprof/", pprof.Index)
  mux.Handle("/debug/pprof/cmdline", pprof.CmdLine)
  mux.Handle("/debug/pprof/profile", pprof.Profile)
  mux.Handle("/debug/pprof/symbol", pprof.Symbol)
  mux.Handle("/debug/pprof/trace", pprof.Trace)

  go func() {
    if err := http.Serve(l, mux); err != nil {
      cancel()
      // log why we shut down the context
      return err
    }
  }()

  // If you want something semi-synchronous, sleep here for a fraction of a second

  return nil
}