2016-09-04 10 views
33

Używam biblioteki Mux z Gorilla Web Toolkit wraz z dołączonym serwerem http Go.Jak zatrzymać http.ListenAndServe()

Problem polega na tym, że w mojej aplikacji serwer HTTP jest tylko jednym komponentem i wymagane jest zatrzymanie i uruchomienie według własnego uznania.

Po wywołaniu http.ListenAndServe(fmt.Sprintf(":%d", service.Port()), service.router) blokuje i nie mogę zatrzymać serwera.

Wiem, że to był problem w przeszłości, czy tak jest nadal? Czy są jakieś nowe rozwiązania? Dzięki.

Odpowiedz

17

Można skonstruować net.Listener

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

które można Close()

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

i http.Serve() na nim

http.Serve(l, service.router) 
+1

dziękuję, ale to nie odpowiada na moje pytanie. Pytam o 'http.ListenAndServe' z określonych powodów. W ten sposób używam biblioteki GWT MUX, nie jestem pewien, jak używać net.listen do tego .. – conor

+4

Używasz http.Serve() zamiast http.ListenAndServe() dokładnie w ten sam sposób z tą samą składnią tylko z własnym Listener. http.Serve (net.Listener, gorilla.mux.Router) – Uvelichitel

+0

Ah wielkie, dzięki. Nie testowałem jeszcze, ale powinienem działać. – conor

9

Aktualizacja:

Jak wspomniano w odpowiedzi yo.ian.g. Program Go 1.8 zawiera tę funkcję w standardowej bibliotece lib.

Original Odpowiedź:

Opierając się na Uvelichitel's odpowiedź.

Można utworzyć własną wersję ListenAndServe, która zwraca wartość io.Closer i nie blokuje.

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 
} 

Pełny kod dostępny here.

serwer HTTP zostanie zamknięty z błędem accept tcp [::]:8080: use of closed network connection

15

Go 1.8 będzie zawierać wdzięku i stanowcze zamknięcie, dostępnego poprzez Server::Shutdown(context.Context) i Server::Close() odpowiednio.

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

srv.Shutdown(context) 

Odpowiedni commit można znaleźć here

+0

Przykro mi, że jestem wybredna i wiem, że kod jest czystym przykładem użycia, ale jako ogólna zasada: 'go func() {X()}()', po którym następuje 'Y()' czyni fałszywe przypuszczenie czytnik, że 'X()' wykona przed 'Y()'. Waitgroups itp. Upewnij się, że takie błędy czasu nie gryzą cię, gdy najmniej się tego spodziewasz! – colminator

34

Regarding wdzięku wyłączanie (wprowadzonego w Go 1.8), trochę bardziej konkretny przykład:

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() { 
     if err := srv.ListenAndServe(); err != nil { 
      // cannot panic, because this probably is an intentional close 
      log.Printf("Httpserver: ListenAndServe() error: %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") 
} 
+0

Tak, ta funkcja to Shutdown(), której konkretne użycie demonstruję tutaj. Dzięki, powinienem być bardziej przejrzysty, zmieniłem tytuł na teraz: "Jeśli chodzi o pełne wdzięku zamknięcie (wprowadzone w Go 1.8), nieco bardziej konkretny przykład:" –

-1

Co robiłem w takich przypadkach, gdy aplikacja jest tylko serwerem i nie wykonuje żadnej innej funkcji, aby zainstalować taki model jak http.HandleFunc dla wzorca takiego jak /shutdown. Coś takiego jak

http.HandleFunc("/shutdown", func(w http.ResponseWriter, r *http.Request) { 
    if <credentials check passes> { 
     fmt.Fprint(w, "Goodbye!\n") 
     os.Exit(0) 
    } 
}) 

Nie wymaga 1.8. Ale jeśli 1.8 jest dostępny, to rozwiązanie to można osadzić tutaj zamiast wywołania os.Exit(0), jeśli jest to pożądane, jak sądzę.

+0

Nie myśl, że to jest dobre rozwiązanie do wdrożenia produkcji. :) –

2

Ponieważ żadna z poprzednich odpowiedzi nie mówi, dlaczego nie możesz tego zrobić, jeśli używasz http.ListenAndServe(), wszedłem do v1.Kod źródłowy 8 http i tutaj jest to, co mówi:

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

Jak widać funkcja http.ListenAndServe nie zwraca zmienną serwera. Oznacza to, że nie możesz dostać się do "serwera", aby użyć polecenia Zamknij. W związku z tym należy utworzyć własną instancję "serwera" zamiast korzystać z tej funkcji, aby umożliwić pełne zamknięcie.