背景介绍

最近在优化公司的一些golang脚本服务,发现脚本服务中有些调用第三方接口的http请求设置超时时间没有生效。大致代码如下:

var (
    TransportDefault = &http.Transport{
        DialContext: (&net.Dialer{
            Timeout:   200 * time.Millisecond,
            KeepAlive: 30 * time.Second,
            DualStack: true,
        }).DialContext,
        MaxIdleConnsPerHost:   1000,
        IdleConnTimeout:       30 * time.Second,
        ExpectContinueTimeout: 3 * time.Second,
        ResponseHeaderTimeout: 3 * time.Second,
    }
)

func NewHttpClient(timeout time.Duration) *http.Client {
    return &http.Client{Transport: TransportDefault, Timeout: timeout}
}

//具体调用
client := NewHttpClient(5*time.Second)
...

执行下来发现真正的超时时间并不是5秒,而是3秒。

调试下来,发现决定 http 请求超时的不仅是 http.Client 结构体中的 Timeout 参数,transport结构体里的 IdleConnTimeoutExpectContinueTimeoutResponseHeaderTimeout 参数都在一定的场景下会影响真正的超时时间。

于是,开始研究一下golang官方的这个http.client的具体实现。一通看下来,发现挺复杂的,目前主要也只梳理了大概流程。

一图胜千言:

golang http.client 实现原理

到这里,关于http请求超时等等很多其他细节点没有梳理:

  • 请求超时逻辑是如何实现的
  • Client 和 Transport 是否是并发安全的,如何实现的
  • idleConn连接池是如何管理的
  • 一个http请求和响应数据在http.client调用的过程中具体是如何流转的
  • 为了尽可能多的支持并发http请求,使用http.client的时候有哪些优化的点

未完,待续…