J'utilise la bibliothèque Mux de Gorilla Web Toolkit avec le serveur Go http fourni.
Le problème est que dans mon application, le serveur HTTP n'est qu'un composant et il est nécessaire de s'arrêter et de démarrer à ma discrétion.
Quand je l'appelle http.ListenAndServe(fmt.Sprintf(":%d", service.Port()), service.router)
bloque et je n'arrive pas à empêcher le serveur de fonctionner.
Je sais que cela a posé problème dans le passé, est-ce toujours le cas? Existe-t-il de nouvelles solutions?
nil
àsrv.Shutdown
je reçoispanic: runtime error: invalid memory address or nil pointer dereference
. Passer à lacontext.Todo()
place fonctionne.Comme mentionné dans
yo.ian.g
la réponse de. Go 1.8 a inclus cette fonctionnalité dans la bibliothèque standard.Exemple minimal pour pour
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.
Réponse originale - Pre Go 1.8:
S'appuyant sur la réponse d'Uvelichitel .
Vous pouvez créer votre propre version
ListenAndServe
dont renvoie unio.Closer
et ne bloque pas.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 }
Code complet disponible ici .
Le serveur HTTP se fermera avec l'erreur
accept tcp [::]:8080: use of closed network connection
la source
Go 1.8 inclura un arrêt progressif et forcé, disponible via
Server::Shutdown(context.Context)
etServer::Close()
respectivement.go func() { httpError := srv.ListenAndServe(address, handler) if httpError != nil { log.Println("While serving HTTP: ", httpError) } }() srv.Shutdown(context)
Le commit correspondant peut être trouvé ici
la source
go func() { X() }()
suivi deY()
fait la fausse hypothèse au lecteur quiX()
s'exécutera avantY()
. Les groupes d'attente, etc. veillent à ce que des bogues de synchronisation comme celui-ci ne vous mordent pas au moment le moins attendu!Vous pouvez construire
net.Listener
l, err := net.Listen("tcp", fmt.Sprintf(":%d", service.Port())) if err != nil { log.Fatal(err) }
que tu peux
Close()
go func(){ //... l.Close() }()
et
http.Serve()
dessusla source
http.ListenAndServe
pour des raisons spécifiques. Voilà comment j'utilise la bibliothèque GWT MUX, je ne sais pas comment utiliser net.listen pour cela ..Étant donné qu'aucune des réponses précédentes ne dit pourquoi vous ne pouvez pas le faire si vous utilisez http.ListenAndServe (), je suis entré dans le code source http v1.8 et voici ce qu'il dit:
func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe() }
Comme vous pouvez le voir, la fonction http.ListenAndServe ne renvoie pas la variable serveur. Cela signifie que vous ne pouvez pas accéder au «serveur» pour utiliser la commande Shutdown. Par conséquent, vous devez créer votre propre instance de «serveur» au lieu d'utiliser cette fonction pour que l'arrêt progressif soit implémenté.
la source
Vous pouvez fermer le serveur en fermant son contexte.
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 }
Et chaque fois que vous êtes prêt à le fermer, appelez:
la source
ctx
toserver.Shutdown
est incorrecte. Le contexte est déjà annulé, il ne sera donc pas arrêté proprement. Vous avez peut-être bien appeléserver.Close
à un arrêt impur. (Pour un arrêt propre, ce code devra être largement retravaillé.Il est possible de résoudre ce problème en utilisant un
context.Context
utilisant unnet.ListenConfig
. Dans mon cas, je ne voulais pas utiliser l ' appel d' unsync.WaitGroup
ou , et plutôt me fier à un (qui était fermé avec un signal).http.Server
Shutdown()
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 }
la source
Ce que je l' ai fait pour les cas où l'application est que le serveur et effectuer aucune autre fonction est d' installer un
http.HandleFunc
pour un motif comme/shutdown
. Quelque chose commehttp.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) } })
Il ne nécessite pas 1.8. Mais si 1.8 est disponible, alors cette solution peut être intégrée ici au lieu de l'
os.Exit(0)
appel si cela est souhaitable, je crois.Le code pour effectuer tout ce travail de nettoyage est laissé comme un exercice pour le lecteur.
Un crédit supplémentaire si vous pouvez dire où ce code de nettoyage pourrait être le plus raisonnablement placé, car je ne recommanderais pas de le faire ici, et comment ce coup de point final devrait provoquer l'appel de ce code.
Plus de crédit supplémentaire si vous pouvez dire où cela
os.exit(0)
appel (ou quelle que soit la sortie de processus que vous choisissez d'utiliser), donné ici à des fins d'illustration uniquement, serait le plus raisonnablement placé.Encore plus de crédit supplémentaire si vous pouvez expliquer pourquoi ce mécanisme de signalisation de processus de serveur HTTP devrait être considéré au-dessus de tous les autres mécanismes de ce type pensés réalisables dans ce cas.
la source