我有一个带有自定义 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 canceled
或 context deadline exceeded
之一。
现在我知道它们在语义上应该是一样的,但我不明白为什么它每次都返回?
Here 是服务器代码,如果您想亲自尝试一下。
答案 0 :(得分:1)
函数net/http/client.setRequestCancel()用于设置请求的取消。三种方式
因为两者使用相同的截止日期,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