go-net/http/client 连接池

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第7天,点击查看活动详情

前言

最近在搞go的项目,发现项目没有使用到http client连接池,让笔者对连接池的必要性、连接池的配置、连接的资损产生了一些思考,除此之外还会给出go http client连接池的示例。

为啥需要连接池

这个和线程池优点类似,主要是创建、销毁会消耗资源和时间,连接还没到应用层所以是tcp的创建和销毁。

tcp创建需要经过三次握手,销毁需要经过四次挥手。消耗的资源主要包括CPU、内存(包括各种数据结构及分配的缓冲区),此外还包括fd、端口号等。

因为DMA的存在,所以对cpu的消耗会比较少,但是具体指令需要CPU告诉DMA控制器,比如传输什么数据、从哪里传输到哪里。此外CPU还会参与TCP头部的解析。

内存可以使用sysctl -A|grep tcp_.mem查看下:

net.ipv4.tcp_rmem = 8192        87380   16777216
net.ipv4.tcp_wmem = 8192        65536   16777216
复制代码

当然需要避免创建和销毁是针对同一个host的,所以连接池是每个host的连接池。

所以使用连接池能避免和同一个host频繁建立和销毁连接。同时也能限制最大连接数,但是服务一般不去限制,因为会导致进来的请求因为没有连接响应时间变长,需要做的是最外层的限流和扩容,做的话可以控制下游/第三方服务的压力,但是下游或者第三方自己应该需要做限流,不需要把控制耦合在上游。

什么时候需要使用httpclient连接池

如果每次请求的host都不一样就没必要用,但是绝大部分服务对外发起的请求都是已知的,不存在随机或者每次都不一样。如果是这样不用管是否请求量,加上肯定比不加好。

不过需要知道的是连接是双向的,发起方维护了连接,接收方也会维护,接收方会因为发起方的维护而占用资源,但是笔者认为接收方如果是第三方的时候肯定会考虑到这点,如果是自己的下游服务也需要做好限流和扩容,如果下游服务接收量有限,发起方服务需要做好限流而不是连接池的问题。

创建连接池

var (
   httpClient *http.Client
)

func init() {
   tr := &http.Transport{
      Proxy: http.ProxyFromEnvironment,
      //InsecureSkipVerify用来控制客户端是否证书和服务器主机名。如果设置为true, 则不会校验证书以及证书中的主机名和服务器主机名是否一致。
      TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
      DialContext: (&net.Dialer{
         Timeout:   10 * time.Second, //拨号等待连接完成的最大时间
         KeepAlive: 30 * time.Second, //保持网络连接活跃keep-alive探测间隔时间。
      }).DialContext,
      MaxIdleConns:        200,
      IdleConnTimeout:     300 * time.Second,
      MaxIdleConnsPerHost: 200,
   }
   httpClient = &http.Client{
      Transport: tr,
      Timeout:   30 * time.Second, //设置超时,包含connection时间、任意重定向时间、读取response body时间
   }
}
复制代码
resp, err := httpClient.Do(req)
复制代码
  • Dialer.Timeout:就是等待Connection的时间,这个网络正常情况下毫秒级别的,为了考虑异常情况设置大点,笔者这里设置了10s。
  • Dialer.KeepAlive:当不传输的时候保持心跳的间隔。
  • Transport.MaxIdleConns:允许的最大空闲连接,可以根据并发平均值去设置,不是越多越好。
  • Transport.IdleConnTimeout:空闲连接超时时间,超过了之后回收,最好根据生产流量去设置,不会让空闲的长期占用资源,又不至于刚销毁流量就来了。
  • Transport.MaxIdleConnsPerHost:如果只有一个下游服务调用就和MaxIdleConns设置成一样。
  • Client.Timeout:包含connection时间、任意重定向时间、读取response body时间,肯定要大于Dialer.Timeout。

注意:最好依据压测和生成真实流量配置。

扫描二维码关注公众号,回复: 14435698 查看本文章

参考

TCP/HTTP连接/Socket/端口

实验说明 Golang HTTP 连接池参数

一次 HttpClient 连接池设置不当,引发服务雪崩

httpclient连接池使用及简单分析 About Pool Sizing

猜你喜欢

转载自juejin.im/post/7127653494030336008