如何停止http.ListenAndServe()

jim*_*jim 67 go

我正在使用Gorilla Web Toolkit中的Mux库以及捆绑的Go http服务器.

问题是在我的应用程序中,HTTP服务器只是一个组件,它需要自行决定停止和启动.

当我称之为http.ListenAndServe(fmt.Sprintf(":%d", service.Port()), service.router)块时,我似乎无法阻止服务器运行.

我知道这在过去一直是个问题,是不是仍然如此?请问有什么新的解决方案吗?谢谢.

joo*_*.fi 69

关于正常关闭(在Go 1.8中引入),更具体的例子:

package main

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

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 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
    }

    log.Printf("main: done. exiting")
}
Run Code Online (Sandbox Code Playgroud)


Joh*_*yil 37

yo.ian.g答案所述.Go 1.8已将此功能包含在标准库中.

for的最小例子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
    }
Run Code Online (Sandbox Code Playgroud)

原始答案 - 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
}
Run Code Online (Sandbox Code Playgroud)

可以使用完整的代码在这里.

HTTP服务器将关闭并显示错误 accept tcp [::]:8080: use of closed network connection


yo.*_*n.g 21

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)
Run Code Online (Sandbox Code Playgroud)

相关提交可以在这里找到

  • 很抱歉挑剔,我知道你的代码纯粹是示例用法,但作为一般规则:`go func(){X()}()`后跟`Y()`对读者做出错误的假设`X()`将在`Y()`之前执行.Waitgroups等确保这样的时间错误不会在最不期望的时候咬你! (6认同)

Uve*_*tel 19

你可以建造 net.Listener

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

你可以 Close()

go func(){
    //...
    l.Close()
}()
Run Code Online (Sandbox Code Playgroud)

http.Serve()在上面

http.Serve(l, service.router)
Run Code Online (Sandbox Code Playgroud)

  • 您使用http.Serve()而不是http.ListenAndServe()与使用自己的Listener完全相同的语法.http.Serve(net.Listener,gorilla.mux.Router) (5认同)

juz*_*juz 11

由于以前的答案都没有说明为什么你不能这样做,如果你使用http.ListenAndServe(),我进入了v1.8 http源代码,这就是它所说的:

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

如您所见,http.ListenAndServe函数不返回服务器变量.这意味着您无法使用"服务器"来使用"关闭"命令.因此,您需要创建自己的"服务器"实例,而不是使用此函数来实现正常关闭.