go的net/http有哪些值得关注的细节? 3

简介: go的net/http有哪些值得关注的细节?

连接池的结构

我们了解到连接池可以复用网络连接,接下来我们通过一个例子来看看网络连接池的结构。

func main() {
    tr := &http.Transport{
        MaxIdleConns:    100,
        IdleConnTimeout: 3 * time.Second,
    }
    n := 5
    for i := 0; i < n; i++ {
        req, _ := http.NewRequest("POST", "http://www.baidu.com", nil)
        req.Header.Add("content-type", "application/json")
        client := &http.Client{
            Transport: tr,
            Timeout:   3 * time.Second,
        }
        resp, _ := client.Do(req)
        _, _ = ioutil.ReadAll(resp.Body)
        _ = resp.Body.Close()
    }
    time.Sleep(time.Second * 1)
    fmt.Printf("goroutine num is %d\n", runtime.NumGoroutine())
}

注意这里请求的不是https,而是http。最终结果输出5,为什么?

这是因为,http://www.baidu.com会返回307,重定向到https://www.baidu.com

http重定向为https

在网络中,我们可以通过一个五元组来唯一确定一个TCP连接。

五元组

它们分别是源ip,源端口,协议,目的ip,目的端口。只有当多次请求的五元组一样的情况下,才有可能复用连接。

放在我们这个场景下,源ip、源端口、协议都是确定的,也就是两次http请求的目的ip或目的端口有区别的时候,就需要使用不同的TCP长连接。

而http用的是80端口,https用的是443端口。于是连接池就为不同的网络目的地建立不同的长连接。

因此最终结果5个goroutine,其实2个goroutine来自http,2个goroutine来自https,1个main goroutine。

我们来看下源码的具体实现。net/http底层通过一个叫idleConnmap去存空闲连接,也就是空闲连接池。


idleConn这个map的key是协议和地址,其实本质上就是ip和端口。map的value是长连接的数组([]*persistConn),说明net/http支持为同一个地址建立多个TCP连接,这样可以提升传输的吞吐。

连接池的结构和逻辑


Transport是什么?

Transport本质上是一个用来控制http调用行为的一个组件,里面包含超时控制,连接池等,其中最重要的是连接池相关的配置。

我们通过下面的例子感受下。

func main() {
    n := 5
    for i := 0; i < n; i++ {
        httpClient := &http.Client{}
        resp, _ := httpClient.Get("https://www.baidu.com")
        _, _ = ioutil.ReadAll(resp.Body)
        _ = resp.Body.Close()
    }
    time.Sleep(time.Second * 1)
    fmt.Printf("goroutine num is %d\n", runtime.NumGoroutine())
}
func main() {
    n := 5
    for i := 0; i < n; i++ {
        httpClient := &http.Client{
            Transport:  &http.Transport{},
        }
        resp, _ := httpClient.Get("https://www.baidu.com")
        _, _ = ioutil.ReadAll(resp.Body)
        _ = resp.Body.Close()
    }
    time.Sleep(time.Second * 1)
    fmt.Printf("goroutine num is %d\n", runtime.NumGoroutine())
}

上面的代码第一个例子的代码会输出3。分别是main goroutine + read goroutine + write goroutine,也就是有一个被不断复用的TCP连接。

在第二例子中,当我们在每次client中都创建一个新的http.Transport,此时就会输出11

说明TCP连接没有复用,每次请求都会产生新的连接。这是因为每个http.Transport内都会维护一个自己的空闲连接池,如果每个client都创建一个新的http.Transport,就会导致底层的TCP连接无法复用。如果网络请求过大,上面这种情况会导致协程数量变得非常多,导致服务不稳定。

因此,最佳实践是所有client都共用一个transport

func main() {
    tr := &http.Transport{
        MaxIdleConns:    100,
        IdleConnTimeout: 3 * time.Second,
    }
    n := 5
    for i := 0; i < n; i++ {
        req, _ := http.NewRequest("POST", "https://www.baidu.com", nil)
        req.Header.Add("content-type", "application/json")
        client := &http.Client{
            Transport: tr,
            Timeout:   3 * time.Second,
        }
        resp, _ := client.Do(req)
        _, _ = ioutil.ReadAll(resp.Body)
        _ = resp.Body.Close()
    }
    time.Sleep(time.Second * 1)
    fmt.Printf("goroutine num is %d\n", runtime.NumGoroutine())
}

如果创建客户端的时候不指定http.Client,会默认所有http.Client都共用同一个DefaultTransport。这一点可以从源码里看出。

默认使用DefaultTransport

DefaultTransport

因此当第二段代码中,每次都重新创建一个Transport的时候,每个Transport内都会各自维护一个空闲连接池。因此每次建立长连接后都会多两个协程(读+写),对应1个main goroutine+(read goroutine + write goroutine)* 5 =11。

目录
相关文章
|
4月前
|
JSON 安全 前端开发
类型安全的 Go HTTP 请求
类型安全的 Go HTTP 请求
|
1月前
|
网络协议 安全 Go
Go语言进行网络编程可以通过**使用TCP/IP协议栈、并发模型、HTTP协议等**方式
【10月更文挑战第28天】Go语言进行网络编程可以通过**使用TCP/IP协议栈、并发模型、HTTP协议等**方式
51 13
|
2月前
|
API
使用`System.Net.WebClient`类发送HTTP请求来调用阿里云短信API
使用`System.Net.WebClient`类发送HTTP请求来调用阿里云短信API
39 0
|
4月前
|
数据采集 缓存 IDE
Go中遇到http code 206和302的获取数据的解决方案
文章提供了解决Go语言中处理HTTP状态码206(部分内容)和302(重定向)的方案,包括如何获取部分数据和真实请求地址的方法,以便程序员能快速完成工作,享受七夕时光。
212 0
Go中遇到http code 206和302的获取数据的解决方案
|
4月前
|
数据采集 API 开发者
.NET 8新特性:使用ConfigurePrimaryHttpMessageHandler定制HTTP请求
在.NET 8中,通过`ConfigurePrimaryHttpMessageHandler`方法,开发者能更精细地控制HTTP请求,这对于构建高效爬虫尤为重要。此特性支持定制代理IP、管理Cookie与User-Agent,结合多线程技术,有效应对网络限制及提高数据采集效率。示例代码展示了如何设置代理服务器、模拟用户行为及并发请求,从而在遵守网站规则的同时,实现快速稳定的数据抓取。
.NET 8新特性:使用ConfigurePrimaryHttpMessageHandler定制HTTP请求
|
4月前
|
数据采集 开发框架 .NET
HttpClient在ASP.NET Core中的最佳实践:实现高效的HTTP请求
在现代Web开发中,高效可靠的HTTP请求对应用性能至关重要。ASP.NET Core提供的`HttpClient`是进行这类请求的强大工具。本文探讨其最佳实践,包括全局复用`HttpClient`实例以避免性能问题,通过依赖注入配置预设头部信息;使用代理IP以防IP被限制;设置合理的`User-Agent`和`Cookie`来模拟真实用户行为,提高请求成功率。通过这些策略,可显著增强爬虫或应用的稳定性和效率。
115 0
HttpClient在ASP.NET Core中的最佳实践:实现高效的HTTP请求
|
4月前
|
JSON 测试技术 Go
Go Kit中读取原始HTTP请求体的方法
Go Kit中读取原始HTTP请求体的方法
|
4月前
|
网络协议 Go
go的net/http有哪些值得关注的细节?
go的net/http有哪些值得关注的细节?
|
Web App开发 新零售 前端开发
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html><head><meta http-equiv="Cont
1.尽可能地了解需求,系统层面适用开闭原则 2.模块化,低耦合,能快速响应变化,也可以避免一个子系统的问题波及整个大系统 3.
752 0
|
Web App开发 前端开发 Java
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html><head><meta http-equiv="Cont
服务端需在vm arguments一栏下加上    -agentlib:jdwp=transport=dt_socket,server=y,address=8000 并以run模式启动 如果以debug模式启动服务端...
725 0