亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

在編寫 http 處理程序時,我們是否必須監聽請求上下文取消?

在編寫 http 處理程序時,我們是否必須監聽請求上下文取消?

Go
牧羊人nacy 2023-03-21 10:12:52
假設我正在編寫一個 http 處理程序,它在返回響應之前執行其他操作,我是否必須設置一個偵聽器來檢查 http 請求上下文是否已被取消?以便它可以立即返回,或者在請求上下文取消時是否有其他方法退出處理程序?func handleSomething(w http.ResponseWriter, r *http.Request) {    done := make(chan error)    go func() {        if err := doSomething(r.Context()); err != nil {            done <- err                        return        }        done <- nil    }()    select {    case <-r.Context().Done():        http.Error(w, r.Context().Err().Error(), http.StatusInternalServerError)        return    case err := <-done:        if err != nil {            http.Error(w, err.Error(), http.StatusInternalServerError)            return        }        w.WriteHeader(http.StatusOK)        w.Write([]byte("ok"))    }}func doSomething(ctx context.Context) error {    // simulate doing something for 1 second.    time.Sleep(time.Second)    return nil}我嘗試對其進行測試,但是在上下文被取消后,doSomething功能并沒有停止并且仍在后臺運行。func TestHandler(t *testing.T) {    mux := http.NewServeMux()    mux.HandleFunc("/something", handleSomething)    srv := http.Server{        Addr:    ":8989",        Handler: mux,    }    var wg sync.WaitGroup    wg.Add(1)    go func() {        defer wg.Done()        if err := srv.ListenAndServe(); err != nil {            log.Println(err)        }    }()    time.Sleep(time.Second)    req, err := http.NewRequest(http.MethodGet, "http://localhost:8989/something", nil)    if err != nil {        t.Fatal(err)    }    cl := http.Client{        Timeout: 3 * time.Second,    }    res, err := cl.Do(req)    if err != nil {        t.Logf("error: %s", err.Error())    } else {        t.Logf("request is done with status code %d", res.StatusCode)    }    go func() {        <-time.After(10 * time.Second)        shutdown, cancel := context.WithTimeout(context.Background(), 10*time.Second)        defer cancel()        srv.Shutdown(shutdown)    }()    wg.Wait()}
查看完整描述

1 回答

?
慕婉清6462132

TA貢獻1804條經驗 獲得超2個贊

我剛剛對提供的解決方案進行了一些重構,現在它應該可以工作了。讓我指導您完成相關更改。


函數doSomething_

func doSomething(ctx context.Context) error {

    fmt.Printf("%v - doSomething: start\n", time.Now())

    select {

    case <-ctx.Done():

        fmt.Printf("%v - doSomething: cancelled\n", time.Now())

        return ctx.Err()

    case <-time.After(3 * time.Second):

        fmt.Printf("%v - doSomething: processed\n", time.Now())

        return nil

    }

}

它等待取消輸入或在延遲幾秒后3返回給調用者。它接受一個上下文來監聽。


函數handleSomething_

func handleSomething(w http.ResponseWriter, r *http.Request) {

    ctx := r.Context()


    fmt.Printf("%v - handleRequestCtx: start\n", time.Now())


    done := make(chan error)

    go func() {

        if err := doSomething(ctx); err != nil {

            fmt.Printf("%v - handleRequestCtx: error %v\n", time.Now(), err)

            done <- err

        }


        done <- nil

    }()


    select {

    case <-ctx.Done():

        fmt.Printf("%v - handleRequestCtx: cancelled\n", time.Now())

        return

    case err := <-done:

        if err != nil {

            fmt.Printf("%v - handleRequestCtx: error: %v\n", time.Now(), err)

            w.WriteHeader(http.StatusInternalServerError)

            return

        }

        fmt.Printf("%v - handleRequestCtx: processed\n", time.Now())

    }

}

在這里,邏輯與您的邏輯非常相似。在 select 中,我們檢查接收到的錯誤是否存在nil,并根據此返回正確的 HTTP 狀態碼給調用者。如果我們收到取消輸入,我們將取消所有上下文鏈。


函數TestHandler_

func TestHandler(t *testing.T) {

    r := mux.NewRouter()

    r.HandleFunc("/demo", handleSomething)


    srv := http.Server{

        Addr:    ":8000",

        Handler: r,

    }


    var wg sync.WaitGroup

    wg.Add(1)

    go func() {

        defer wg.Done()

        if err := srv.ListenAndServe(); err != nil {

            fmt.Println(err.Error())

        }

    }()


    ctx := context.Background()

    ctx, cancel := context.WithTimeout(ctx, 1*time.Second) // request canceled

    // ctx, cancel := context.WithTimeout(ctx, 5*time.Second) // request processed

    defer cancel()


    req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "http://localhost:8000/demo", nil)


    client := http.Client{}

    res, err := client.Do(req)

    if err != nil {

        fmt.Println(err.Error())

    } else {

        fmt.Printf("res status code: %d\n", res.StatusCode)

    }

    srv.Shutdown(ctx)


    wg.Wait()

}

在這里,我們啟動了一個 HTTP 服務器并通過http.Client. 可以看到有兩條語句設置上下文超時。如果您使用帶有評論的那個// request canceled,一切都將被取消,否則,如果您使用另一個,請求將被處理。

我希望這能澄清你的問題!


查看完整回答
反對 回復 2023-03-21
  • 1 回答
  • 0 關注
  • 96 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號