robert-min / handson-go

Go-lang hands on guide
0 stars 0 forks source link

Chapter7. Service HTTP Server - client disconnection, set timeout for all handler #21

Open robert-min opened 1 year ago

robert-min commented 1 year ago

클라이언트 연결 끊김 처리

robert-min commented 1 year ago

클라이언트 연결 끊김 처리 코드

func handlePing(w http.ResponseWriter, r *http.Request) {
    log.Println("ping: Got a request")
    time.Sleep(3 * time.Second)
    fmt.Fprintf(w, "pong")
}

func doSomeWork(data []byte) {
    time.Sleep(5 * time.Second)
}

func handleUserAPI(w http.ResponseWriter, r *http.Request) {
    done := make(chan bool)

    log.Println("I started processing the request")

    req, err := http.NewRequestWithContext(
        r.Context(),
        "GET",
        "http://localhost:8080/ping", nil,
    )
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    client := &http.Client{}
    log.Println("Outgoing HTTP request")
    resp, err := client.Do(req)
    if err != nil {
        log.Printf("Error making request: %v\n", err)
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    defer resp.Body.Close()
    data, _ := io.ReadAll(resp.Body)

    log.Println("Processing the response i got")

    go func() {
        doSomeWork(data)
        done <- true
    }()

    select {
    case <-done:
        log.Println("doSomeWork done: Continuing request processing")
    case <-r.Context().Done():
        log.Printf("Aborting request processing: %v\n", r.Context().Err())
        return
    }

    fmt.Fprintf(w, string(data))
    log.Println("I finished procesing the request")

}

func main() {
    listenAddr := os.Getenv("LISTEN_ADDR")
    if len(listenAddr) == 0 {
        listenAddr = ":8080"
    }

    timeoutDuration := 30 * time.Second

    userHandler := http.HandlerFunc(handleUserAPI)
    hTimeout := http.TimeoutHandler(
        userHandler,
        timeoutDuration,
        "I ran out of time",
    )

    mux := http.NewServeMux()
    mux.Handle("/api/users", hTimeout)
    mux.HandleFunc("/ping", handlePing)

    log.Fatal(http.ListenAndServe(":8080", mux))
}
robert-min commented 1 year ago

서버 모든 핸들러 함수에 타임아웃 구현

robert-min commented 1 year ago

서버 모든 핸들러에 타임아웃 구현 코드


func handleUserAPi(w http.ResponseWriter, r *http.Request) {
    log.Println("I started processing the request")
    defer r.Body.Close()

    data, err := io.ReadAll(r.Body)
    if err != nil {
        log.Printf("Error reading body %v\n", err)
        http.Error(
            w, "Error reading body", http.StatusInternalServerError,
        )
        return
    }

    log.Println(string(data))
    fmt.Fprintf(w, "Hello world!")
    log.Println("I finsiehd processing")
}

func main() {
    listenAddr := os.Getenv("LISTEN_ADDR")
    if len(listenAddr) == 0 {
        listenAddr = ":8080"
    }

    mux := http.NewServeMux()
    mux.HandleFunc("/api/users", handleUserAPi)

    s := http.Server{
        Addr:         listenAddr,
        Handler:      mux,
        ReadTimeout:  5 * time.Second,
        WriteTimeout: 5 * time.Second,
    }
    log.Fatal(s.ListenAndServe())
}
robert-min commented 1 year ago

우아한 서버 종료

robert-min commented 1 year ago

우아한 서버 종료 구현


func handleUserAPI(w http.ResponseWriter, r *http.Request) {
    log.Println("I started processing the request")
    defer r.Body.Close()

    data, err := io.ReadAll(r.Body)
    if err != nil {
        log.Printf("Error reading body: %v\n", err)
        http.Error(
            w, "Error reading body",
            http.StatusInternalServerError,
        )
        return
    }
    log.Println(string(data))
    fmt.Fprintf(w, "Hello world!")
    log.Println("I finished processing the request")
}

func shutDown(ctx context.Context, s *http.Server, waitForShutdownCompletion chan struct{}) {
    sigch := make(chan os.Signal, 1)
    signal.Notify(sigch, syscall.SIGINT, syscall.SIGTERM)
    sig := <-sigch
    log.Printf("Got signal: %v . Server shutting down.", sig)

    // shutdown 매서드가 현재 서버 상에 존재하는 요청을 처리하는 동안 최대로 기다릴 시간
    childCtx, cancel := context.WithTimeout(
        ctx, 30*time.Second,
    )
    defer cancel()

    if err := s.Shutdown(childCtx); err != nil {
        log.Printf("Error during shutdown: %v", err)
    }
    waitForShutdownCompletion <- struct{}{}
}

func main() {
    listenAddr := os.Getenv("LISTEN_ADDR")
    if len(listenAddr) == 0 {
        listenAddr = ":8080"
    }

    mux := http.NewServeMux()
    mux.HandleFunc("/api/users", handleUserAPI)

    s := http.Server{
        Addr:    listenAddr,
        Handler: mux,
    }

    waitForShutdownCompletion := make(chan struct{})
    go shutDown(context.Background(), &s, waitForShutdownCompletion)

    err := s.ListenAndServe()
    log.Print(
        "Wainting for shutdown to complete...",
    )
    <-waitForShutdownCompletion
    log.Fatal(err)
}