HTTP.ListenAndServe () को कैसे रोकें


90

मैं गोल्ड वेब टूलकिट के साथ गोरिल्ला वेब टूलकिट से एमएक्स लाइब्रेरी का उपयोग कर रहा हूं।

समस्या यह है कि मेरे आवेदन में HTTP सर्वर केवल एक घटक है और इसे मेरे विवेक पर रोकना और शुरू करना आवश्यक है।

जब मैं http.ListenAndServe(fmt.Sprintf(":%d", service.Port()), service.router)इसे ब्लॉक करता हूं और मैं सर्वर को चलने से रोक नहीं सकता।

मुझे पता है कि यह अतीत में एक समस्या रही है, क्या अभी भी ऐसा ही है? क्या कोई नया उपाय है?

जवाबों:


92

सुशोभित शटडाउन के बारे में (गो 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")
}

1
हां, सुविधा शटडाउन () है, जिसका ठोस उपयोग मैं यहां प्रदर्शित कर रहा हूं। धन्यवाद, मुझे अधिक स्पष्ट होना चाहिए था, मैंने अब इस शीर्षक को बदल दिया: "सुशोभित शटडाउन के बारे में (गो 1.8 में शुरू), थोड़ा और अधिक ठोस उदाहरण:"
joonas.fi

जब मैं पास हो nilजाता srv.Shutdownहूं panic: runtime error: invalid memory address or nil pointer dereferencecontext.Todo()काम करने के बजाय पासिंग ।
हुब्रो

1
@ हूब्रो अजीब है, मैंने सिर्फ नवीनतम गोलांग संस्करण (1.10) पर यह कोशिश की, और यह ठीक चला। संदर्भ.बैकग्राउंड () या संदर्भ। TODO () बेशक काम करता है और अगर यह आपके लिए काम करता है, तो अच्छा। :)
joonas.fi

1
@ newplayer65 ऐसा करने के कई तरीके हैं। एक तरीका यह हो सकता है कि सिंक बनाया जा सके। मुख्य रूप से वैटग्रुप (), उस पर Add (1) को कॉल करें और उसके लिए एक पॉइंटर पास करें। ListenAndServe ()। इसके बाद बस प्रतीक्षा करें। मुख्य के अंत में प्रतीक्षा करें। () मुख्य कार्य के लिए प्रतीक्षा करें () अपनी नौकरी समाप्त करने के लिए प्रतीक्षा करें।
joonas.fi

1
@ newplayer65 मैंने आपके कोड को देखा। एक चैनल का उपयोग करना मेरे सुझाव से बेहतर, शायद बेहतर विकल्प है। मेरा कोड मुख्य रूप से शटडाउन () प्रदर्शित करने के लिए था - उत्पादन गुणवत्ता कोड नहीं दिखा रहा था :) Ps आपकी परियोजना का "सर्वर गोफर" लोगो एडॉर्ब्स है! : D
joonas.fi

70

जैसा कि yo.ian.gउत्तर में बताया गया है । गो 1.8 ने इस कार्यक्षमता को मानक लिब में शामिल किया है।

इसके लिए न्यूनतम उदाहरण 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.

मूल उत्तर - प्री गो 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


मैंने एक पैकेज बनाया है जो आपके लिए बॉयलरप्लेट करता है github.com/pseidemann/finish
pseidemann

24

गो 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)

प्रासंगिक प्रतिबद्धता यहां पाई जा सकती है


7
खेदजनक होने के लिए खेद है, और मुझे पता है कि आप कोड विशुद्ध रूप से उदाहरण उपयोग थे, लेकिन एक सामान्य नियम के रूप में: go func() { X() }()इसके बाद Y()पाठक को झूठी धारणा बनाता है जो X()पहले निष्पादित करेगा Y()। वेटग्रुप आदि सुनिश्चित करते हैं कि इस तरह की बगिंग आपको कम से कम अपेक्षित होने पर काटती नहीं है!
colm.anseo

20

आप निर्माण कर सकते हैं 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)

1
धन्यवाद, लेकिन यह मेरे सवाल का जवाब नहीं है। मैं http.ListenAndServeविशिष्ट कारणों के बारे में पूछ रहा हूं । यही कारण है कि मैं GWT MUX लाइब्रेरी का उपयोग करता हूं, मुझे यकीन नहीं है कि इसके लिए net.listen का उपयोग कैसे किया जाए ..
jim

6
आप HTTP.Sist () के बजाय http.ListenAndServe () का उपयोग करते हैं, ठीक उसी तरह सिंटैक्स के साथ जिस तरह से केवल श्रोता के साथ। http.Serve (net.Listener, gorilla.mux। राउटर)
Uvelichitel

आह महान, धन्यवाद। मैंने अभी तक परीक्षण नहीं किया है, लेकिन काम करना चाहिए।
जिम

1
थोड़ा देर से, लेकिन हम इस उपयोग के मामले के लिए शिष्टाचार पैकेज का उपयोग कर रहे हैं। यह मानक http पैकेज के लिए एक ड्रॉप-इन प्रतिस्थापन है जो सुशोभित शटडाउन की अनुमति देता है (यानी, नए लोगों को गिराने के दौरान सभी सक्रिय अनुरोधों को समाप्त करता है, फिर बाहर निकलता है)।
21

13

चूंकि पिछले उत्तर में से कोई भी नहीं कहता है कि आप ऐसा क्यों नहीं कर सकते हैं यदि आप http.ListenAndServe () का उपयोग करते हैं, तो मैं v1.8 http स्रोत कोड में चला गया और यहां यह कहता है:

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

जैसा कि आप देख सकते हैं http.ListenAndServe फ़ंक्शन सर्वर चर को वापस नहीं करता है। इसका मतलब है कि आपको शटडाउन कमांड का उपयोग करने के लिए 'सर्वर' नहीं मिल सकता है। इसलिए, आपको कार्यान्वित किए जाने वाले सुशोभित शटडाउन के लिए इस फ़ंक्शन का उपयोग करने के बजाय अपना स्वयं का 'सर्वर' उदाहरण बनाने की आवश्यकता है।


2

आप इसके संदर्भ को बंद करके सर्वर को बंद कर सकते हैं।

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()

"सर्वर शट डाउन हो रहा कुछ बुरा ffs जाओ नहीं है ..." :)
पॉल नोफ

ध्यान देने योग्य एक बात यह है कि एक सुंदर शटडाउन के लिए, बाहर निकलने से पहले आपको शटडाउन के लौटने की प्रतीक्षा करने की आवश्यकता है जो यहां नहीं हो रहा है।
मार्सिन बिल्स्की

का आपके द्वारा उपयोग ctxकरने के लिए server.Shutdownसही नहीं है। संदर्भ पहले से ही रद्द है, इसलिए इसे सफाई से बंद नहीं किया जाएगा । आपको server.Closeअशुद्ध शटडाउन के लिए अच्छी तरह से बुलाया जा सकता है । (एक साफ शटडाउन के लिए इस कोड को बड़े पैमाने पर फिर से काम करना होगा।
डेव C

0

context.Contextका उपयोग करके इसे हल करना संभव है net.ListenConfig। मेरे मामले में, मैं एक का उपयोग नहीं करना चाहता था sync.WaitGroupया http.Serverकी Shutdown()कॉल और इसके बदले एक पर भरोसा करते हैं 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
}

-6

मैंने ऐसे मामलों के लिए क्या किया है, जहां आवेदन सिर्फ सर्वर है और कोई अन्य कार्य नहीं कर रहा है http.HandleFuncजैसे पैटर्न के लिए स्थापित किया गया है /shutdown। कुछ इस तरह

http.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)
    }
})

इसके लिए 1.8 की आवश्यकता नहीं है। लेकिन अगर 1.8 उपलब्ध है, तो उस समाधान को os.Exit(0)कॉल के बजाय यहां एम्बेड किया जा सकता है यदि वांछनीय है, तो मुझे विश्वास है।

उस सफाई कार्य को करने के लिए कोड को पाठक के लिए एक अभ्यास के रूप में छोड़ दिया जाता है।

अतिरिक्त क्रेडिट यदि आप कह सकते हैं कि जहां सफाई कोड सबसे उचित रूप से रखा जा सकता है, क्योंकि मैं इसे यहां करने की सिफारिश नहीं करूंगा, और यह समापन बिंदु हिट उस कोड के आह्वान का कारण कैसे होना चाहिए।

अधिक अतिरिक्त श्रेय यदि आप कह सकते हैं कि उस os.exit(0)कॉल (या जो भी प्रक्रिया आप उपयोग करने के लिए चुनते हैं) को केवल इलस्ट्रेटिव उद्देश्यों के लिए यहां दिया गया है, तो इसे सबसे उचित रूप से रखा जाएगा।

फिर भी और भी अधिक अतिरिक्त क्रेडिट अगर आप क्यों की व्याख्या कर सकते इस HTTP सर्वर प्रक्रिया संकेतन की व्यवस्था अन्य सभी तरह के तंत्र ऊपर विचार किया जाना चाहिए इस मामले में व्यावहारिक सोचा।


निश्चित रूप से, मैंने इस प्रश्न का उत्तर दिया, क्योंकि समस्या की प्रकृति के बारे में किसी भी अन्य धारणा के बिना, और विशेष रूप से, किसी भी उत्पादन वातावरण के बारे में कोई धारणा नहीं है। लेकिन मेरे स्वयं के संपादन के लिए, @MarcinBilski, वास्तव में इस समाधान को किसी भी पर्यावरण, उत्पादन या अन्यथा के लिए एक अच्छा फिट नहीं होने की क्या आवश्यकता होगी?
greg.carter

2
इसका मतलब यह है कि किसी भी चीज़ की तुलना में अधिक जीभ-इन-गाल क्योंकि यह स्पष्ट है कि आपके पास उत्पादन ऐप में एक / बंद हैंडलर नहीं होगा। :) मुझे लगता है कि आंतरिक टूलींग के लिए कुछ भी जाता है। इसके अलावा, सर्वर को इनायत से बंद करने के तरीके हैं, इसलिए यह अचानक से कनेक्शन को नहीं गिराता है या डेटाबेस लेनदेन के माध्यम से मध्य-मार्ग को खराब करता है या, डिस्क आदि को लिखते समय और भी खराब होता है
Marcin Bilski

निश्चित रूप से, यह मामला नहीं हो सकता है कि नीचे के मतदाता अकल्पनीय हैं। यह होना चाहिए कि मैं बहुत अधिक कल्पना की कल्पना करता हूं। मैंने अपनी त्रुटि को सुधारने के लिए प्रतिक्रिया (उदाहरण सहित) को अद्यतन किया है।
greg.carter
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.