কীভাবে http.ListenAndServe বন্ধ করা যায় ()


91

আমি গন্ডিলা ওয়েব টুলকিট থেকে বান্ডিলযুক্ত গো HT সার্ভারের সাথে Mux গ্রন্থাগারটি ব্যবহার করছি।

সমস্যাটি হ'ল আমার আবেদনে এইচটিটিপি সার্ভারটি কেবল একটি উপাদান এবং এটি আমার বিবেচনার ভিত্তিতে থামানো এবং শুরু করা প্রয়োজন।

যখন আমি http.ListenAndServe(fmt.Sprintf(":%d", service.Port()), service.router)এটিকে ব্লক বলি এবং সার্ভারটি চলমান থেকে থামাতে পারি না।

আমি সচেতন যে অতীতে এই সমস্যা ছিল, এখনও কি তাই না? কোন নতুন সমাধান আছে?

উত্তর:


92

কৌতূহলী শাটডাউন (Go 1.8 তে প্রবর্তিত) সম্পর্কিত, আরও কিছুটা দৃ concrete় উদাহরণ:

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")
}

4
হ্যাঁ, বৈশিষ্ট্যটি হ'ল শাটডাউন (), যার কংক্রিট ব্যবহার আমি এখানে প্রদর্শন করছি। ধন্যবাদ, আমার আরও স্পষ্ট হওয়া উচিত ছিল, আমি এখনই এই শিরোনামটি পরিবর্তন করেছি: "
গ্রেফিউল

আমি যখন পাস nilকরার srv.Shutdownআমি পেতে panic: runtime error: invalid memory address or nil pointer dereferencecontext.Todo()পরিবর্তে পাসিং কাজ করে।
হুব্রো

4
@ হুব্রো খুব অদ্ভুত, আমি এটি সর্বশেষতম গোলং সংস্করণে (১.১০) চেষ্টা করে দেখেছি এবং তা ভালই গেছে। প্রসঙ্গ.ব্যাকগ্রাউন্ড () বা প্রসঙ্গে T :)
junas.fi

4
@ newplayer65 এর একাধিক উপায় রয়েছে ways একটি উপায় হ'ল সিঙ্ক তৈরি করা যেতে পারে mainউইটগ্রুপকে মূল () এ, এড (1) এ কল করুন এবং এটিতে একটি পয়েন্টারটি শুরু করুন HTTPServer () এবং ডিফার ওয়েটারগ্রুপ ডোন () কল করুন যে গোরআউটিনের শুরুতে কল রয়েছে শুনুন এবং সার্ভার ()। তারপরে জোরআউটিনের কাজ শেষ হওয়ার অপেক্ষা করতে প্রধান () এর শেষে ওয়েটগ্রুপ.উইট () কে কল করুন।
junas.fi

4
@ newplayer65 আমি আপনার কোডটি দেখেছি। চ্যানেল ব্যবহার করা আমার পরামর্শের চেয়ে ভাল, সম্ভবত ভাল বিকল্প। আমার কোডটি ছিল প্রধানত শাটডাউন () - উত্পাদন মানের কোড প্রদর্শন নয় :) আপনার প্রকল্পের "সার্ভার গোফার" লোগোটি অ্যাডোরবস! : D
junas.fi

70

যেমনটি yo.ian.gউত্তর দেওয়া হয়েছে। Go 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:

উভিলিচিটেলের উত্তরে বিল্ডিং ।

আপনি আপনার নিজস্ব সংস্করণ তৈরি করতে পারেন 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
}

এখানে সম্পূর্ণ কোড উপলব্ধ ।

এইচটিটিপি সার্ভার ত্রুটি সহ বন্ধ হবে accept tcp [::]:8080: use of closed network connection


আমি একটি প্যাকেজ তৈরি করেছি যা আপনার জন্য বয়লারপ্লেট করে github.com/pseidemann/finish
pseidemann

24

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

4
ধন্যবাদ তবে এটি আমার প্রশ্নের উত্তর দেয় না। আমি http.ListenAndServeনির্দিষ্ট কারণে জিজ্ঞাসা করছি । এভাবেই আমি জিডব্লিউটি এমউএক্স লাইব্রেরি ব্যবহার করি, আমি নিশ্চিত না যে কীভাবে নেট.লাস্টেন ব্যবহার করব ..
জিম

6
আপনি ঠিক নিজের শ্রোতার সাথে একই সিনট্যাক্সের সাথে ঠিক একইভাবে http.ListenAndServe () এর পরিবর্তে http.Serve () ব্যবহার করেন। http.Serve (নেট.লাইস্টনার, গরিলা.ম্যাক্স.রউটার)
ইউভিলিচিটেল

আহা, ধন্যবাদ আমি এখনও পরীক্ষা করিনি তবে কাজ করা উচিত।
জিম

4
বিট দেরিতে, তবে আমরা এই ব্যবহারের ক্ষেত্রে শিষ্টাচার প্যাকেজটি ব্যবহার করছি। এটি স্ট্যান্ডার্ড HT প্যাকেজটির জন্য একটি ড্রপ-ইন প্রতিস্থাপন যা প্রশংসনীয় শাটডাউনকে অনুমতি দেয় (যেমন নতুনগুলি অস্বীকার করার সময় সমস্ত সক্রিয় অনুরোধগুলি শেষ করে, তারপরে প্রস্থান করবে)।
কেডিস 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()

"সার্ভার বন্ধ করা কোনও খারাপ এফএফএস গো ... নয় :) :)
পল নফ্ফ

একটি বিষয় লক্ষণীয় যে, প্রশংসনীয় শাটডাউন করার জন্য, প্রস্থান করার আগে আপনাকে শাটডাউন ফিরে আসার অপেক্ষা করতে হবে যা এখানে ঘটছে বলে মনে হয় না।
মার্সিন বিলস্কি

আপনার ব্যবহার ctxকরতে server.Shutdownভুল। প্রসঙ্গটি ইতিমধ্যে বাতিল হয়েছে সুতরাং এটি পরিষ্কারভাবে বন্ধ করা হবে না । আপনি হয়ত server.Closeঅশুচি বন্ধের ডাক দিয়েছিলেন for (ক্লিন শাটডাউন করার জন্য এই কোডটি আরও ব্যাপকভাবে পুনরায় কাজ করা দরকার
ডেভ সি

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)কেবলমাত্র উদাহরণস্বরূপ উদ্দেশ্যে এখানে দেওয়া কলটি (বা আপনি যে কোনও প্রক্রিয়াটি প্রস্থান করার জন্য বেছে নিতে পারেন) ণদান সবচেয়ে যুক্তিযুক্তভাবে স্থাপন করা হবে।

তবুও আরও অতিরিক্ত ক্রেডিট যদি আপনি ব্যাখ্যা করতে পারেন যে এইচটিটিপি সার্ভার প্রক্রিয়া সংকেতকরণের এই পদ্ধতিটিকে এই ক্ষেত্রে কার্যকর হিসাবে বিবেচিত অন্যান্য সমস্ত প্রক্রিয়াগুলির তুলনায় বিবেচনা করা উচিত।


অবশ্যই, আমি সমস্যার প্রকৃতি এবং বিশেষত, প্রদত্ত কোনও উত্পাদন পরিবেশ সম্পর্কে কোনও অনুমান ছাড়া কোনও অনুমান ছাড়াই জিজ্ঞাসা করা প্রশ্নের উত্তর দিয়েছি। তবে আমার নিজের উত্সর্গের জন্য, @ মার্সিনবিলস্কি, ঠিক কী কী প্রয়োজনীয়তা এই সমাধানটিকে কোনও পরিবেশ, উত্পাদন বা অন্যথায় উপযুক্ত মানাবে না?
গ্রেগকার্টার

4
এটি যে কোনও কিছুর চেয়ে বেশি জিহ্বা-ইন-গাল বোঝায় কারণ এটি স্পষ্ট যে আপনার কাছে কোনও প্রোডাকশন অ্যাপ্লিকেশনটিতে / শাটডাউন হ্যান্ডলারটি থাকবে না। :) অভ্যন্তরীণ সরঞ্জামদানের জন্য আমার মনে হয় কিছু হয়। এদিকে, সার্ভারটি নিখুঁতভাবে বন্ধ করার উপায় রয়েছে যাতে এটি হঠাৎ কোনও সংযোগ না ফেলে বা মাঝ পথে ক্রাশ না করে বা আরও খারাপ, ডিস্কে লেখার সময়
মার্সিন বিলস্কি

অবশ্যই, এটি হ'ল ডাউন ভোটাররা অকল্পনীয়। এটি এমন যে আমি খুব বেশি কল্পনা করে নিই। আমি আমার ত্রুটিটি সংশোধন করতে প্রতিক্রিয়াটি (উদাহরণ সহ) আপডেট করেছি।
গ্রেগকার্টার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.