1. Go语言http.Transport的介绍
在Go语言的标准库中,http包提供了HTTP协议的客户端和服务端的实现。其中,http.Transport是HTTP客户端的核心实现之一,负责管理连接、对HTTP请求进行编码与发送、对HTTP响应进行解析和处理。
http.Transport的结构体定义如下:
type Transport struct {
Proxy func(*Request) (*url.URL, error)
DialContext func(ctx context.Context, network, addr string) (net.Conn, error)
Dial func(network, addr string) (net.Conn, error)
DialTLS func(network, addr string, cfg *tls.Config) (net.Conn, error)
DialTLSContext func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error)
TLSHandshakeTimeout time.Duration
DisableKeepAlives bool
DisableCompression bool
MaxIdleConns int
MaxIdleConnsPerHost int
MaxConnsPerHost int
IdleConnTimeout time.Duration
ResponseHeaderTimeout time.Duration
ExpectContinueTimeout time.Duration
TLSClientConfig *tls.Config
TLSNextProto map[string]func(authority string, c *tls.Conn) RoundTripper
ProxyConnectHeader Header
MaxResponseHeaderBytes int64
WriteBufferSize int
ReadBufferSize int
ForceAttemptHTTP2 bool
MaxResponseBodySize int64
BufferPool BufferPool
}
2. 连接管理与复用
2.1 连接管理方式
http.Transport的连接管理采用了与传统HTTP客户端不同的方式。它会维护一个可复用的连接池(Conncection Pool),以便于重复使用已经建立的TCP连接,避免每次都重新建立TCP连接的开销。这个Connection Pool包含了多个维护TCP连接的线程(goroutine),它们会检测空闲连接的有效期,关闭非空闲连接,以及创建新的连接。
2.2 连接池设置
http.Transport会对创建的每个连接进行一些基本的限制。例如,它会限制可用于复用的空闲连接的数量。可以通过MaxIdleConns和MaxIdleConnsPerHost来控制连接的复用。其中MaxIdleConns控制整个连接池中空闲的连接数,而MaxIdleConnsPerHost则控制每个主机(或者说是每个 IP)的连接数。
在http.Transport中,连接使用完后(即响应写入成功后)会被放回连接池。连接池并不会阻止连接的关闭,这意味着它不会持有任何连接。如果一个连接关闭了,上一个响应中的HTTP流没有完全被消费,但所期望的响应主体的大小已经是已知的,连接关闭时,连接会被丢弃,因为它的响应已经被部分失效。
另外,http.Transport还可以设置连接的有效期。HTTP连接有时会被一些中间代理(例如网关、防火墙等)关闭,因此实际连接的寿命可能小于MaxIdleConns/MaxIdleConnsPerHost参数所设置的时间。保留断开或关闭一段时间后重新打开连接的能力可重用具体 Go 代码不允许重用的连接。
2.3 优化Keep-Alive
在HTTP协议中,Keep-Alive是一项基于HTTP/1.0协议的性能优化技术,它可以重用已经建立的连接,避免每次都重新建立TCP连接。在HTTP/1.1中官方也推荐使用这项技术,因此在我们使用http.Transport进行开发时,有必要对Keep-Alive进行优化。
http.Transport中的Keep-Alive有两个参数可以调整:DisableKeepAlives和IdleConnTimeout。DisableKeepAlives默认为false,即开启Keep-Alive。IdleConnTimeout则是空闲连接的最大时间限制。通过设置它为一个合适的值,可以防止长时间的空闲连接占用连接池的连接数。
2.4 连接重用的例子
func reuseConnExample() error{
tr := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
MaxIdleConns: 20,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
}
client := &http.Client{Transport: tr}
for i := 0; i < 5; i++ {
res, err := client.Get("https://example.com/")
if err != nil {
return err
}
defer res.Body.Close()
// ...
}
return nil
}
3. 连接复用
3.1 会话复用
为了避免每次传送请求时都建立连接的开销,http.Transport还可以进行会话复用。会话复用在HTTP/1.1中是默认开启的,它可以通过同一个TCP连接在不同的HTTP请求之间发送。这可以为既保持HTTP客户端的高效性,又大大降低了网络流量。
3.2 重复请求的复用检测
在Go语言的http.Transport中,http.RoundTripper接口是HTTP请求的核心接口。它负责将HTTP请求发送给某个服务器,并返回响应。http.RoundTripper接口默认的实现就是http.Transport。
http.Transport中的默认实现方法是使用HTTP/1.1的keep-alive机制来复用连接。这个机制可以应对绝大多数的情况,但是有些情况下它并不起作用。例如,在下载一个大文件时,当前的响应可能没有完全接收,同时(某些)服务器也不愿意重用当前的连接。但是,我们可以通过request.Context()中的context.Deadline及context.CancelFunc来取消或中断正在进行的请求进行复用控制。
3.3 多路复用(HTTP/2)
HTTP/2基于SPDY协议,它在HTTP 1.x基础上进行了优化,比如采用二进制协议格式、多路复用、头部压缩等改进。其中,多路复用的实现可以让多个请求在同一TCP连接上并行完成,从而达到提高HTTP并行度、减少TCP连接的效果。
而Go语言标准库的http.Transport默认支持HTTP/2协议,当我们使用http.Client向HTTPS网站发送请求时,如果服务器支持,http.Transport会自动使用HTTP/2协议进行通信。
3.4 连接复用示例
func reuseSessionExample() error {
client := &http.Client{
Transport: &http.Transport{},
}
res1, err := client.Get("https://example.com/")
if err != nil {
return fmt.Errorf("error sending request: %v", err)
}
//...
res2, err := client.Get("https://example.com/")
if err != nil {
return fmt.Errorf("error sending request: %v", err)
}
// ...
return nil
}
4. 总结
本文介绍了Go语言http.Transport的连接管理和复用技巧。通过对http.Transport的结构体定义、连接管理方式、连接池设置、优化Keep-Alive、会话复用、重复请求的复用检测和多路复用的介绍,我们可以更好地理解http.Transport的工作原理及优化方法。这些技巧不仅可以提高HTTP客户端的性能,还可以节省网络流量,从而提高系统的整体性能。