0

我有一个带有自定义的 HTTP 客户端,RoundTripper它反过来使用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 canceledor之一context deadline exceeded
现在我知道它们在语义上应该是相同的,但我不明白为什么它会在何时返回?

如果您想自己尝试,这里是服务器代码。

4

1 回答 1

2

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

  • 第二个会返回:net/http:request cancelled
  • 第三个将返回:超出上下文期限

因为两者都使用相同的截止日期,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
于 2021-02-25T14:20:44.403 回答