我有一个运行基本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服务器/执行任何清理?
答案 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()