背景介绍
最近在优化公司的一些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结构体里的 IdleConnTimeout、ExpectContinueTimeout、ResponseHeaderTimeout 参数都在一定的场景下会影响真正的超时时间。
于是,开始研究一下golang官方的这个http.client的具体实现。一通看下来,发现挺复杂的,目前主要也只梳理了大概流程。
一图胜千言:
到这里,关于http请求超时等等很多其他细节点没有梳理:
- 请求超时逻辑是如何实现的
- Client 和 Transport 是否是并发安全的,如何实现的
- idleConn连接池是如何管理的
- 一个http请求和响应数据在http.client调用的过程中具体是如何流转的
- 为了尽可能多的支持并发http请求,使用http.client的时候有哪些优化的点