1. 简介
网络传输一般可以使用 Go 标准库中的 http
或者第三方库实现。在高并发的场景下,http.Transport
是一个非常好的选择,可以优化连接上的复用与流量的控制,降低网络传输的开销。本文将详细介绍如何使用 Go 和 http.Transport
进行高效的网络传输。
2. 安装
在使用之前需要先导入 http
和 net/http
,打开终端输入以下命令:
go get -u net/http
3. http.Transport
3.1 基本用法
使用 http.Transport
可以直接在客户端进行 HTTP 请求。首先需要创建一个 http.Client
对象,然后设置其中的 transport 属性为实例化的 transport 对象。
package main
import (
"fmt"
"net/http"
)
func main() {
transport := &http.Transport{}
client := &http.Client{
Transport: transport,
}
resp, err := client.Get("https://www.google.com")
if err != nil {
fmt.Println(err)
}
defer resp.Body.Close()
fmt.Println(resp.StatusCode)
}
运行上面的程序可以返回 200 状态码,表示连接成功。如果返回其他状态码则说明连接失败。
3.2 设置代理
有时候,我们需要访问没有被墙的站点,可以使用代理。设置代理可以直接在 http.Transport
对象中设置。代码如下:
package main
import (
"fmt"
"net/http"
"net/url"
)
func main() {
proxyUrl, err := url.Parse("http://localhost:1080")
if err != nil {
fmt.Println(err)
}
transport := &http.Transport{
Proxy: http.ProxyURL(proxyUrl),
}
client := &http.Client{
Transport: transport,
}
resp, err := client.Get("https://www.google.com")
if err != nil {
fmt.Println(err)
}
defer resp.Body.Close()
fmt.Println(resp.StatusCode)
}
这里设置了一个代理地址,需要将代理地址解析成 *url.URL
对象后,设置到 http.Transport
对象中的 Proxy
属性里即可。
3.3 连接池
默认情况下,在创建 http.Transport
对象时会创建 2 个空闲连接。如果在同一时间只有一个 goroutine 进行请求,那么连接既不会被重用也不会被抢占。在实际应用中我们需要调整这些设置,以防止出现瓶颈。可以在实例化http.Transport
对象时设置MaxIdleConns
设置连接池的大小,可以在不同的场景下对这个参数进行优化。
package main
import (
"fmt"
"net/http"
"net/url"
)
func main() {
transport := &http.Transport{
MaxIdleConns: 10,
IdleConnTimeout: 30,
}
client := &http.Client{
Transport: transport,
}
resp, err := client.Get("https://www.google.com")
if err != nil {
fmt.Println(err)
}
defer resp.Body.Close()
fmt.Println(resp.StatusCode)
}
4. 性能优化
4.1 多路复用
Go 1.6 之后的版本默认开启了 http.Client
的 KeepAlive
选项,这可以保持客户端与服务端对同一域名的 HTTP 连接复用,但是一次只能处理一个 HTTP 请求。如果需要同时处理多个 HTTP 请求,就要使用 HTTP/2,首先需要确保使用的是 HTTPS 协议,因为 HTTP/2 只能在 HTTPS 上使用。其次需要在客户端和服务端都启用 HTTP/2,代码修改如下:
package main
import (
"crypto/tls"
"fmt"
"net/http"
)
func main() {
transport := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
Proxy: http.ProxyFromEnvironment,
}
client := &http.Client{
Transport: transport,
}
resp, err := client.Get("https://www.google.com")
if err != nil {
fmt.Println(err)
}
defer resp.Body.Close()
fmt.Println(resp.StatusCode)
}
上述修改代码中 InsecureSkipVerify:true 只是为了在测试时不验证证书,实际中应该使用正确的证书验证。
4.2 压缩和解压缩
在网络传输中启用压缩和解压缩可以减轻网络传输的开销。在 Go 的请求标准库中可以使用标准库 httpgzip
实现压缩和解压缩功能。代码如下:
package main
import (
"fmt"
"net/http"
"github.com/daaku/go.httpgzip"
)
func main() {
transport := &http.Transport{}
client := &http.Client{
Transport: httpgzip.NewTransport(transport),
}
resp, err := client.Get("https://www.google.com")
if err != nil {
fmt.Println(err)
}
defer resp.Body.Close()
fmt.Println(resp.StatusCode)
}
5. 总结
通过使用以上技巧可以充分利用 Go 标准库的优点,在高并发情况下对网络传输进行优化,提高程序的效率。