HTTP 客户端在超时时返回随机错误

时间:2021-02-24 09:25:25

标签: http go go-http

我有一个带有自定义 RoundTripper 的 HTTP 客户端,它反过来使用 http.DefaultTransport 来处理请求。 现在想象我有一个缓慢的服务器,它需要很长时间才能响应,它使我的 http 客户端超时并取消客户端。这是客户端的代码:

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "time"
)

type rt struct {
    roundTripper func(req *http.Request) (*http.Response, error)
}

func (r rt) RoundTrip(req *http.Request) (*http.Response, error) {
    return r.roundTripper(req)
}

func main() {

    c := http.Client{
        Timeout:   3 * time.Second,
        Transport:  rt{RoundTripper(http.DefaultTransport)},
    }
    resp, err := c.Get("http://127.0.0.1:9000")
    if err != nil {
        fmt.Println("err:", err)
    } else {
        body, err := ioutil.ReadAll(resp.Body)
        resp.Body.Close()
        fmt.Println(string(body), err)
    }

}
func RoundTripper(next http.RoundTripper) func(req *http.Request) (*http.Response, error) {
    return func(req *http.Request) (*http.Response, error) {
        resp, err := next.RoundTrip(req)
        if err != nil {
            return nil, fmt.Errorf("err: %w", err)
        }
        return resp, nil
    }
}

这里的问题是我在超时时收到的错误是随机的 net/http: request canceledcontext deadline exceeded 之一。
现在我知道它们在语义上应该是一样的,但我不明白为什么它每次都返回?

Here 是服务器代码,如果您想亲自尝试一下。

1 个答案:

答案 0 :(得分:1)

函数net/http/client.setRequestCancel()用于设置请求的取消。三种方式

  • 第二个将返回:net/http:请求取消
  • 第三个将返回:超出上下文截止日期

因为两者使用相同的截止日期,time.now()+client.Timeout。 所以会根据运行时的调度,通过这两种方式随机取消请求。

    https://github.com/golang/go/blob/master/src/net/http/transport.go#L2652

    case <-cancelChan:
        // return err: net/http: request
        pc.t.CancelRequest(req.Request) canceled
        cancelChan = nil
    case <-ctxDoneChan:
        // return err:
        pc.t.cancelRequest(req.Request, req.Context().Err())
        cancelChan = nil
        ctxDoneChan = nil