Saya menggunakan pustaka Mux dari Gorilla Web Toolkit bersama dengan server http Go yang dibundel.
Masalahnya adalah bahwa dalam aplikasi saya, server HTTP hanya satu komponen dan diperlukan untuk berhenti dan memulai menurut kebijaksanaan saya.
Ketika saya menyebutnya http.ListenAndServe(fmt.Sprintf(":%d", service.Port()), service.router)
blok dan sepertinya saya tidak bisa menghentikan server agar tidak berjalan.
Saya sadar ini telah menjadi masalah di masa lalu, apakah masih demikian? Apakah ada solusi baru?
nil
kesrv.Shutdown
saya dapatkanpanic: runtime error: invalid memory address or nil pointer dereference
. Mengopercontext.Todo()
malah berhasil.Seperti yang disebutkan dalam
yo.ian.g
jawaban. Go 1.8 telah memasukkan fungsionalitas ini dalam lib standar.Contoh minimal untuk
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.
Jawaban Asli - Pre Go 1.8:
Membangun jawaban Uvelichitel .
Anda dapat membuat versi Anda sendiri
ListenAndServe
yang mengembalikanio.Closer
dan tidak memblokir.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 }
Kode lengkap tersedia di sini .
Server HTTP akan ditutup dengan kesalahan
accept tcp [::]:8080: use of closed network connection
sumber
Go 1.8 akan mencakup shutdown yang anggun dan paksa, tersedia melalui
Server::Shutdown(context.Context)
danServer::Close()
masing - masing.go func() { httpError := srv.ListenAndServe(address, handler) if httpError != nil { log.Println("While serving HTTP: ", httpError) } }() srv.Shutdown(context)
Komit yang relevan dapat ditemukan di sini
sumber
go func() { X() }()
diikuti denganY()
membuat asumsi palsu kepada pembaca yangX()
akan dieksekusi sebelumnyaY()
. Waitgroups dll. Memastikan kesalahan waktu seperti ini tidak mengganggu Anda saat tidak diharapkan!Anda bisa membangun
net.Listener
l, err := net.Listen("tcp", fmt.Sprintf(":%d", service.Port())) if err != nil { log.Fatal(err) }
yang Anda bisa
Close()
go func(){ //... l.Close() }()
dan
http.Serve()
di atasnyasumber
http.ListenAndServe
alasan tertentu. Begitulah cara saya menggunakan perpustakaan GWT MUX, saya tidak yakin cara menggunakan net.listen untuk itu ..Karena tidak ada jawaban sebelumnya yang mengatakan mengapa Anda tidak dapat melakukannya jika Anda menggunakan http.ListenAndServe (), saya masuk ke kode sumber http v1.8 dan inilah yang dikatakan:
func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe() }
Seperti yang Anda lihat, fungsi http.ListenAndServe tidak mengembalikan variabel server. Ini berarti Anda tidak bisa masuk ke 'server' untuk menggunakan perintah Shutdown. Oleh karena itu, Anda perlu membuat instance 'server' Anda sendiri alih-alih menggunakan fungsi ini agar shutdown yang tepat dapat diimplementasikan.
sumber
Anda dapat menutup server dengan menutup konteksnya.
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 }
Dan kapan pun Anda siap untuk menutupnya, panggil:
sumber
ctx
untukserver.Shutdown
salah. Konteksnya sudah dibatalkan sehingga tidak akan bisa dimatikan dengan bersih. Anda mungkin telah menyerukanserver.Close
penutupan yang tidak bersih. (Untuk pemadaman yang bersih, kode ini perlu dikerjakan ulang secara ekstensif.Hal ini dimungkinkan untuk menyelesaikan ini dengan
context.Context
menggunakan anet.ListenConfig
. Dalam kasus saya, saya tidak ingin menggunakansync.WaitGroup
atauhttp.Server
'sShutdown()
panggilan, dan bukan mengandalkan padacontext.Context
(yang ditutup dengan sinyal).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 }
sumber
Apa yang telah saya lakukan untuk kasus seperti itu di mana aplikasi hanya server dan tidak melakukan fungsi lain adalah menginstal
http.HandleFunc
untuk pola seperti/shutdown
. Sesuatu sepertihttp.HandleFunc("/shutdown", func(w http.ResponseWriter, r *http.Request) { if <credentials check passes> { // - Turn on mechanism to reject incoming requests. // - Block until "in-flight" requests complete. // - Release resources, both internal and external. // - Perform all other cleanup procedures thought necessary // for this to be called a "graceful shutdown". fmt.Fprint(w, "Goodbye!\n") os.Exit(0) } })
Ini tidak membutuhkan 1.8. Tetapi jika 1.8 tersedia, maka solusi itu dapat disematkan di sini alih-alih
os.Exit(0)
panggilan jika diinginkan, saya yakin.Kode untuk melakukan semua pekerjaan pembersihan itu dibiarkan sebagai latihan untuk pembaca.
Kredit tambahan jika Anda dapat mengatakan di mana kode pembersihan itu mungkin paling masuk akal ditempatkan, karena saya tidak akan merekomendasikan melakukannya di sini, dan bagaimana titik akhir ini harus menyebabkan pemanggilan kode itu.
Lebih banyak kredit ekstra jika Anda dapat mengatakan di mana
os.exit(0)
panggilan itu (atau proses keluar apa pun yang Anda pilih untuk digunakan), yang diberikan di sini hanya untuk tujuan ilustrasi, akan ditempatkan paling masuk akal.Namun bahkan lebih ekstra kredit jika Anda bisa menjelaskan mengapa ini mekanisme HTTP Server proses signaling harus dipertimbangkan di atas semua mekanisme lain seperti berpikir bisa diterapkan dalam kasus ini.
sumber