正常关闭多台服务器

时间:2017-04-18 13:19:29

标签: http go tcp

我有一个运行基本HTTP服务器的应用程序,并且还接受通过TCP的连接。

基本伪代码如下:

package main

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

func main() {
    // create serve HTTP server.
    serveSvr := http.NewServeMux()
    serveSvr.HandleFunc("/", handler())

    // create server error channel
    svrErr := make(chan error)

    // start HTTP server.
    go func() {
        svrErr <- http.ListenAndServe(":8080", serveSvr)
    }()

    // start TCP server
    go func() {
        lnr, err := net.Listen("tcp", ":1111")
        if err != nil {
            svrErr <- err
            return
        }
        defer lnr.Close()

        for {
            conn, err := lnr.Accept()
            if err != nil {
                log.Printf("connection error: %v", err)
                continue
            }
            // code to handle each connection
        }
    }()

    select {
    case err := <-svrErr:
        log.Print(err)
    }
}

我在不同的goroutine中运行两个服务器,如果其中任何一个失败,我需要一种方法来优雅地关闭它们。例如;如果HTTP服务器出错,我将如何返回并关闭TCP服务器/执行任何清理?

1 个答案:

答案 0 :(得分:1)

首先保留对http服务器和tcp侦听器的引用,以便稍后关闭它们。

创建单独的错误通道,以便您知道哪条路径返回了错误,并缓冲它们以便始终完成发送。

要确保在退出之前完成要尝试的任何清理,可以将WaitGroup添加到服务器goroutines。

我的示例的简单扩展可能如下所示:

var wg sync.WaitGroup

// create  HTTP server.
serveSvr := http.NewServeMux()
serveSvr.HandleFunc("/", handler())
server := &http.Server{Addr: ":8080", Handler: serveSvr}

// create http server error channel
httpErr := make(chan error, 1)

// start HTTP server.
wg.Add(1)
go func() {
    defer wg.Done()
    httpErr <- server.ListenAndServe()
    // http cleanup
}()

tcpErr := make(chan error, 1)
listener, err := net.Listen("tcp", ":1111")
if err != nil {
    tcpErr <- err
} else {
    // start TCP server
    wg.Add(1)
    go func() {
        defer wg.Done()
        defer listener.Close()
        for {
            conn, err := listener.Accept()
            if err != nil {
                if ne, ok := err.(net.Error); ok && ne.Temporary() {
                    // temp error, wait and continue
                    continue
                }
                tcpErr <- err

                // cleanup TCP
                return
            }

            // code to handle each connection
        }
    }()
}
select {
case err := <-httpErr:
    // handle http error and close tcp listen
    if listener != nil {
        listener.Close()
    }
case err := <-tcpErr:
    // handle tcp error and close http server
    server.Close()
}

// you may also want to receive the error from the server
// you shutdown to log

// wait for any final cleanup to finish
wg.Wait()