번들 Go http 서버와 함께 Gorilla Web Toolkit의 Mux 라이브러리를 사용하고 있습니다.
문제는 내 응용 프로그램에서 HTTP 서버가 하나의 구성 요소 일 뿐이며 재량에 따라 중지하고 시작해야한다는 것입니다.
내가 http.ListenAndServe(fmt.Sprintf(":%d", service.Port()), service.router)
그것을 블록 이라고 부르면 서버가 실행되는 것을 막을 수 없습니다.
나는 이것이 과거에 문제 였다는 것을 알고 있는데, 여전히 그럴까요? 새로운 솔루션이 있습니까?
답변
정상 종료 (Go 1.8에 도입 됨)와 관련하여 좀 더 구체적인 예 :
package main
import (
"context"
"io"
"log"
"net/http"
"sync"
"time"
)
func startHttpServer(wg *sync.WaitGroup) *http.Server {
srv := &http.Server{Addr: ":8080"}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "hello world\n")
})
go func() {
defer wg.Done() // let main know we are done cleaning up
// always returns error. ErrServerClosed on graceful close
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
// unexpected error. port in use?
log.Fatalf("ListenAndServe(): %v", err)
}
}()
// returning reference so caller can call Shutdown()
return srv
}
func main() {
log.Printf("main: starting HTTP server")
httpServerExitDone := &sync.WaitGroup{}
httpServerExitDone.Add(1)
srv := startHttpServer(httpServerExitDone)
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
}
// wait for goroutine started in startHttpServer() to stop
httpServerExitDone.Wait()
log.Printf("main: done. exiting")
}
답변
yo.ian.g
의 답변 에서 언급했듯이 . Go 1.8은이 기능을 표준 lib에 포함 시켰습니다.
에 대한 최소 예 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
}
// Wait for ListenAndServe goroutine to close.
원래 답변-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
}
HTTP 서버가 오류와 함께 닫힙니다.
accept tcp [::]:8080: use of closed network connection
답변
답변
당신은 건설 할 수 있습니다 net.Listener
l, err := net.Listen("tcp", fmt.Sprintf(":%d", service.Port()))
if err != nil {
log.Fatal(err)
}
당신이 할 수있는 Close()
go func(){
//...
l.Close()
}()
그리고 http.Serve()
그것에
http.Serve(l, service.router)
답변
이전 답변 중 http.ListenAndServe ()를 사용하면 왜 할 수 없는지 말하지 않았기 때문에 v1.8 http 소스 코드로 들어가서 다음과 같이 말합니다.
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
보시다시피 http.ListenAndServe 함수는 서버 변수를 반환하지 않습니다. 즉, Shutdown 명령을 사용하기 위해 ‘서버’에 액세스 할 수 없습니다. 따라서 정상적인 종료를 구현하려면이 기능을 사용하는 대신 고유 한 ‘서버’인스턴스를 만들어야합니다.
답변
컨텍스트를 닫아 서버를 닫을 수 있습니다.
type ServeReqs func(ctx context.Context, cfg Config, deps ReqHandlersDependencies) error
var ServeReqsImpl = func(ctx context.Context, cfg Config, deps ReqHandlersDependencies) error {
http.Handle(pingRoute, decorateHttpRes(pingHandlerImpl(deps.pingRouteResponseMessage), addJsonHeader()))
server := &http.Server{Addr: fmt.Sprintf(":%d", cfg.port), Handler: nil}
go func() {
<-ctx.Done()
fmt.Println("Shutting down the HTTP server...")
server.Shutdown(ctx)
}()
err := server.ListenAndServeTLS(
cfg.certificatePemFilePath,
cfg.certificatePemPrivKeyFilePath,
)
// Shutting down the server is not something bad ffs Go...
if err == http.ErrServerClosed {
return nil
}
return err
}
닫을 준비가되면 다음으로 전화하십시오.
ctx, closeServer := context.WithCancel(context.Background())
err := ServeReqs(ctx, etc)
closeServer()
답변
또한 이것은 사용 해결할 수있다 context.Context
사용 a net.ListenConfig
. 제 경우에는 sync.WaitGroup
또는 http.Server
의 Shutdown()
호출 을 사용하고 싶지 않았고 대신 a context.Context
(신호로 닫힘) 에 의존했습니다 .
import (
"context"
"http"
"net"
"net/http/pprof"
)
func myListen(ctx context.Context, cancel context.CancelFunc) error {
lc := net.ListenConfig{}
ln, err := lc.Listen(ctx, "tcp4", "127.0.0.1:6060")
if err != nil {
// wrap the err or log why the listen failed
return err
}
mux := http.NewServeMux()
mux.Handle("/debug/pprof/", pprof.Index)
mux.Handle("/debug/pprof/cmdline", pprof.CmdLine)
mux.Handle("/debug/pprof/profile", pprof.Profile)
mux.Handle("/debug/pprof/symbol", pprof.Symbol)
mux.Handle("/debug/pprof/trace", pprof.Trace)
go func() {
if err := http.Serve(l, mux); err != nil {
cancel()
// log why we shut down the context
return err
}
}()
// If you want something semi-synchronous, sleep here for a fraction of a second
return nil
}