前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >golang net/http 连接池

golang net/http 连接池

原创
作者头像
用户8750609
修改2021-11-13 13:45:05
7.1K0
修改2021-11-13 13:45:05
举报
文章被收录于专栏:golang云原生golang云原生

背景

代码语言:javascript
复制
使用golang进行业务开发时,通常会遇到需要发起HTTP调用请求,用于获取业务所需的数据进行下一步处理。
golang在标准库中直接提供了net/http包,通过这个包可以很方便的去发起一个HTTP请求。
对于golang的net/http库其使用通常有两种方式:
1. 使用DefaultClient;2. 使用自定义Client。下面来看看两种方式的用法

net/http使用

1. 使用DefalutClient

代码语言:javascript
复制
对于没有高并发的场景下,使用DefaultClient十分简单,能够快速达到目的。下面看一个示例:
代码语言:javascript
复制
resp, err := http.Get("http://www.example.com")
if err != nil {
	log.Fatal(err)
}
defer resp.Body.Close()

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
	fmt.Println("read body err:", err)
}

fmt.Println("body:", string(body))
代码语言:javascript
复制
这里直接调用net/http封装提供的Get函数,标准库中还封装提供了Post,Head函数。
这些封装函数背后都是使用的DefaultClient。下面看看Get函数的源码:
代码语言:javascript
复制
func Get(url string) (resp *Response, err error) {
	return DefaultClient.Get(url)
}
代码语言:javascript
复制
DefaultClient 是一个全局Client结构,其定义如下:
代码语言:javascript
复制
// DefaultClient is the default Client and is used by Get, Head, and Post.
var DefaultClient = &Client{}

2. 使用自定义Client

代码语言:javascript
复制
实际应用中,为应对各种不同的场景通常需要自定以http Client来满足要求,实现目的。
下面看看自定义client的常见用法:
  • 超时设置
代码语言:javascript
复制
    client := http.Client{
        Timeout: 10 * time.Second,
    }
  • 代理设置
代码语言:javascript
复制
    proxy, _ := url.Parse(proxyUrl)
	tr := &http.Transport{
		Proxy:           http.ProxyURL(proxy),
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}

	client := &http.Client{
		Transport: tr,
		Timeout:   time.Second * 5, //超时时间
	}

	resp, err := client.Get(webUrl)
	if err != nil {
		fmt.Println("出错了", err)
		return
	}

	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	fmt.Println(string(body))
  • 连接池设置
代码语言:javascript
复制
    proxy := func(_ *http.Request) (*url.URL, error) {
				return url.Parse("http://" + netproxy.GetAddr())
			}
	httpTransport = &http.Transport{
					Proxy: proxy,
					DialContext: (&net.Dialer{
						Timeout:   30 * time.Second,
						KeepAlive: 30 * time.Second,
					}).DialContext,
					ForceAttemptHTTP2:     true,
					MaxIdleConns:          100,
					IdleConnTimeout:       90 * time.Second,
					TLSHandshakeTimeout:   10 * time.Second,
					ExpectContinueTimeout: 1 * time.Second,
				}
	httpClient = &http.Client{
				Transport: httpTransport,
			}
代码语言:javascript
复制
实际应用中client通常是作为全局变量来使用,初始化一次即可。无需每次请求都重新定义一次,
因为client中底层使用的transport是一个连接池,不同请求会取用一条不同的连接。

连接池

代码语言:javascript
复制
上面说到,http client中transport是一个连接池,其无论是DefaultClient还是自定义Client都是用到的。
默认的transport定义如下:
代码语言:javascript
复制
    // DefaultTransport is the default implementation of Transport and is
    // used by DefaultClient. It establishes network connections as needed
    // and caches them for reuse by subsequent calls. It uses HTTP proxies
    // as directed by the $HTTP_PROXY and $NO_PROXY (or $http_proxy and
    // $no_proxy) environment variables.
    var DefaultTransport RoundTripper = &Transport{
    	Proxy: ProxyFromEnvironment,
    	DialContext: (&net.Dialer{
    		Timeout:   30 * time.Second,
    		KeepAlive: 30 * time.Second,
    	}).DialContext,
    	ForceAttemptHTTP2:     true,
    	MaxIdleConns:          100,
    	IdleConnTimeout:       90 * time.Second,
    	TLSHandshakeTimeout:   10 * time.Second,
    	ExpectContinueTimeout: 1 * time.Second,
    }
    
    // DefaultMaxIdleConnsPerHost is the default value of Transport's
    // MaxIdleConnsPerHost.
    const DefaultMaxIdleConnsPerHost = 2
代码语言:javascript
复制
如果在http client中没有设置transport属性,那么它就会使用默认的transport。在默认中最大的空闲连接数为100,
每个Host最大空闲数为2. 但是默认的配置中只有关于空闲连接的配置,在实际大量并发的情况下会创建很多连接,
进而导致性能急剧下降。
如果需要控制合适的连接数,就需要使用自定义的client和transport,通常根据应用场景需要调整配置参数。
代码语言:javascript
复制
    type HTTPTransportParam struct {
    	MaxIdleConnsPerHost int
    	MaxIdleConns        int
    	MaxConnsPerHost     int
    	IdleConnTimeout     int
    	DialTimeout         int
    	KeepAlive           int
    }
    var httpTrans *HTTPTransportParam
    
    httpTransport = &http.Transport{
		Proxy: proxy,
		DialContext: (&net.Dialer{
			Timeout:   time.Duration(httpTrans.DialTimeout) * time.Millisecond,
			KeepAlive: time.Duration(httpTrans.KeepAlive) * time.Millisecond,
		}).DialContext,
		ForceAttemptHTTP2:     true,
		MaxIdleConns:          httpTrans.MaxIdleConns,
		MaxIdleConnsPerHost:   httpTrans.MaxIdleConnsPerHost,
		MaxConnsPerHost:       httpTrans.MaxConnsPerHost,
		IdleConnTimeout:       time.Duration(httpTrans.IdleConnTimeout) * time.Millisecond,
		TLSHandshakeTimeout:   10 * time.Second,
		ExpectContinueTimeout: 1 * time.Second,
	}
	
	httpClient = &http.Client{
		Transport: httpTransport,
	}
代码语言:javascript
复制
一般比较关心的参数分两部分:context参数和连接参数
context参数是指建立连接时涉及的参数包括超时,保活参数
连接参数是指与pool相关的参数包括:最大空闲连接数,最大连接数和空闲超时时间。
尤其是最大连接数,默认是没有限制的,如果并发量大的时候会引起大量的连接,进而导致性能下降。
所以需要根据实际情况合理调整最大连接数参数的配置。另外,这里的最大连接数也只是针对单个host的限制,
暂时没找到限制总连接数及主机连接池的控制入口。

net/http的连接池是默认全局共用的,假如后端主机虽然只有一百多台,如果我有100个协程,
有概率会出现同时针对一主机并发访问,那么一个主机就有100个连接,100个后端主机就会产生10000个连接。 
这问题的概率在生产环境中经常出现。100台没问题,那么更多呢?  单ip在主动连接可用的端口不到65535的…  
所以,大家也要考虑到这问题。 

简单的做法可以在进程的连接数做计数,当达到一定的阈值后,进行短连接请求,  但这样带来的问题是time wait过多,
重复的建连效率也在下降。

总结

代码语言:go
复制
golang的标准库中提供了很多使用的库,但在实际使用时需要注意应用场景及合适的使用方式。
默认情况的配置通常只是适用与功能实现,对性能有要求的场景通常需要仔细考量和分析具体的使用方式。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • net/http使用
    • 1. 使用DefalutClient
      • 2. 使用自定义Client
      • 连接池
      • 总结
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
      http://www.vxiaotou.com