Estou usando a biblioteca Mux do Gorilla Web Toolkit junto com o servidor Go http incluído.
O problema é que em meu aplicativo o servidor HTTP é apenas um componente e é necessário parar e iniciar conforme meu critério.
Quando eu chamo, http.ListenAndServe(fmt.Sprintf(":%d", service.Port()), service.router)
ele bloqueia e não consigo parar a execução do servidor.
Sei que isso foi um problema no passado, ainda é o caso? Existem novas soluções?
nil
parasrv.Shutdown
eu consigopanic: runtime error: invalid memory address or nil pointer dereference
. Emcontext.Todo()
vez disso, passar funciona.Conforme mencionado na
yo.ian.g
resposta de. Go 1.8 incluiu esta funcionalidade na biblioteca padrão.Exemplo mínimo para
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.
Resposta original - Pre Go 1.8:
Com base na resposta de Uvelichitel .
Você pode criar sua própria versão do
ListenAndServe
que retorna umio.Closer
e não bloqueia.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 }
Código completo disponível aqui .
O servidor HTTP fechará com o erro
accept tcp [::]:8080: use of closed network connection
fonte
O Go 1.8 incluirá desligamento normal e forçado, disponível via
Server::Shutdown(context.Context)
eServer::Close()
respectivamente.go func() { httpError := srv.ListenAndServe(address, handler) if httpError != nil { log.Println("While serving HTTP: ", httpError) } }() srv.Shutdown(context)
O commit relevante pode ser encontrado aqui
fonte
go func() { X() }()
seguido porY()
faz a falsa suposição para o leitor queX()
irá executar antesY()
. Waitgroups etc. garantem que bugs de tempo como este não o mordam quando menos se espera!Você pode construir
net.Listener
l, err := net.Listen("tcp", fmt.Sprintf(":%d", service.Port())) if err != nil { log.Fatal(err) }
qual você pode
Close()
go func(){ //... l.Close() }()
e
http.Serve()
sobre issofonte
http.ListenAndServe
por motivos específicos. É assim que eu uso a biblioteca GWT MUX, não tenho certeza de como usar net.listen para isso ..Como nenhuma das respostas anteriores diz por que você não pode fazer isso se usar http.ListenAndServe (), fui para o código-fonte http v1.8 e aqui está o que diz:
func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe() }
Como você pode ver, a função http.ListenAndServe não retorna a variável do servidor. Isso significa que você não pode acessar o 'servidor' para usar o comando Desligar. Portanto, você precisa criar sua própria instância de 'servidor' em vez de usar esta função para que o desligamento normal seja implementado.
fonte
Você pode fechar o servidor fechando seu contexto.
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 }
E quando você estiver pronto para fechá-lo, ligue:
fonte
ctx
paraserver.Shutdown
está incorreto. O contexto já foi cancelado, portanto, não será encerrado corretamente. Você bem poderia ter pedidoserver.Close
um desligamento impuro. (Para um desligamento limpo, este código precisaria ser amplamente retrabalhado.É possível resolver isso
context.Context
usando umnet.ListenConfig
. No meu caso, eu não queria usar umsync.WaitGroup
ouhttp.Server
deShutdown()
chamada, e em vez disso dependem de umcontext.Context
(que foi fechado com um sinal).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 }
fonte
O que fiz para esses casos em que o aplicativo é apenas o servidor e não realizando nenhuma outra função foi instalar um
http.HandleFunc
para um padrão como/shutdown
. Algo comohttp.HandleFunc("/shutdown", func(w http.ResponseWriter, r *http.Request) { if <credentials check passes> { // - Turn on mechanism to reject incoming requests. // - Block until "in-flight" requests complete. // - Release resources, both internal and external. // - Perform all other cleanup procedures thought necessary // for this to be called a "graceful shutdown". fmt.Fprint(w, "Goodbye!\n") os.Exit(0) } })
Não requer 1.8. Mas se 1.8 estiver disponível, então essa solução pode ser incorporada aqui em vez da
os.Exit(0)
chamada, se desejável, acredito.O código para realizar todo esse trabalho de limpeza é deixado como um exercício para o leitor.
Crédito extra se você puder dizer onde esse código de limpeza pode ser colocado de maneira mais razoável, pois eu não recomendaria fazê-lo aqui e como essa ocorrência de endpoint deve causar a invocação desse código.
Mais crédito extra se você puder dizer onde essa
os.exit(0)
chamada (ou qualquer saída de processo que você escolher usar), fornecida aqui apenas para fins ilustrativos, seria mais razoavelmente colocada.Ainda mais crédito extra se você puder explicar por que esse mecanismo de sinalização de processo do servidor HTTP deve ser considerado acima de todos os outros mecanismos considerados viáveis neste caso.
fonte