139 votes

Comment arrêter http.ListenAndServe ()

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 qu’il est nécessaire de s’arrêter et de démarrer à ma discrétion.

Lorsque j'appelle http.ListenAndServe(fmt.Sprintf(":%d", service.Port()), service.router) il se bloque et je n'arrive pas à empêcher le serveur de fonctionner.

Je sais que cela a été un problème dans le passé, est-ce toujours le cas? Y at-il de nouvelles solutions s'il vous plaît? Merci.

128voto

joonas.fi Points 183

En ce qui concerne l'arrêt en douceur (introduit dans Go 1.8), un exemple un peu plus concret:

 package main

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

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

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

    go func() {
        // returns ErrServerClosed on graceful close
        if err := srv.ListenAndServe(); err != http.ErrServerClosed {
            // NOTE: there is a chance that next line won't have time to run,
            // as main() doesn't wait for this goroutine to stop. don't use
            // code with race conditions like these for production. see post
            // comments below on more discussion on how to handle this.
            log.Fatalf("ListenAndServe(): %s", err)
        }
    }()

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

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

    srv := startHttpServer()

    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 instead of nil as a https://golang.org/pkg/context/
    if err := srv.Shutdown(nil); err != nil {
        panic(err) // failure/timeout shutting down the server gracefully
    }

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

126voto

John S Perayil Points 3007

Comme mentionné en yo.ian.g's réponse. Aller 1.8 a inclus cette fonctionnalité dans la lib 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, _ := context.WithTimeout(context.Background(), 5*time.Second)
    if err := server.Shutdown(ctx); err != nil {
        // handle err
    }

Réponse Originale À Cette Question - Pré Aller 1.8 :

Bâtiment sur Uvelichitel de réponse.

Vous pouvez créer votre propre version d' ListenAndServe qui retourne un io.Closer et ne se 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 terminerai avec l'erreur accept tcp [::]:8080: use of closed network connection

28voto

yo.ian.g Points 301

Go 1.8 inclura un arrêt gracieux et puissant, disponible via respectivement Server::Shutdown(context.Context) et Server::Close() .

 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

23voto

Uvelichitel Points 5368

Vous pouvez construire net.Listener

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

que vous pouvez Close()

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

et http.Serve() dessus

 http.Serve(l, service.router)
 

21voto

juz Points 421

Comme 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 constater, 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 d'arrêt. Par conséquent, vous devez créer votre propre instance de "serveur" au lieu d'utiliser cette fonction pour la mise en œuvre de l'arrêt progressif.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X