介绍

Golang如何实现连接池的方法?这个问题可能是我们日常学习或工作经常见到的。希望通过这个问题能让你收获颇深。下面是小编给大家带来的参考内容,让我们一起来看看吧!


作为一名Golang开发者,线上环境遇到过好几次连接数暴增问题(mysql/复述,卡夫卡等)。

纠其原因,Golang作为常驻进程,请求第三方服务或者资源完毕后,需要手动关闭连接,否则连接会一直存在。而很多时候,开发者不一定记得关闭这个连接。

这样是不是很麻烦?于是有了连接池。顾名思义,连接池就是管理连接的,我们从连接池获取连接,请求完毕后再将连接还给连接池;连接池帮我们做了连接的建立,复用以及回收工作。

在设计与实现连接池时,我们通常需要考虑以下几个问题:


我们以Golang HTTP连接池为例,分析连接池的实现原理。


交通结构定义如下:

类型运输结构{//操作空闲连接需要获取锁
  idleMu sync.Mutex//空闲连接池、关键为协议目标地址等组合
  idleConn地图[connectMethodKey] [] * persistConn//最近使用结束//等待空闲连接的队列,基于切片实现,队列大小无限制
  idleConnWait地图connectMethodKey wantConnQueue//getConns等待//排队等待建立连接需要获取锁
  connsPerHostMu sync.Mutex//每个主机建立的连接数
  connsPerHost地图connectMethodKey int//等待建立连接的队列,同样基于切片实现,队列大小无限制
  connsPerHostWait地图connectMethodKey wantConnQueue//getConns等待//最大空闲连接数
  MaxIdleConns int//每个目标主机最大空闲连接数;默认为2(注意默认值)
  MaxIdleConnsPerHost int//每个主机可建立的最大连接数
  MaxConnsPerHost int//连接多少时间没有使用则被关闭
  IdleConnTimeout time.Duration//禁用长连接,使用短连接
  DisableKeepAlives bool
  }

可以看的到,连接护着队列,都是一个地图结构,而主要为协议目标地址等组合,即同一种协议与同一个目标主机可建立的连接或者空闲连接是有限制的。

需要特别注意的是,MaxIdleConnsPerHost默认等于2,即与目标主机最多只维护两个空闲连接。这会导致什么呢?

如果遇到突发流量,瞬间建立大量连接,但是回收连接时,由于最大空闲连接数的限制,该联机不能进入空闲连接池,只能直接关闭。结果是,一直新建大量连接,又关闭大量连,业务机器的TIME_WAIT连接数随之突增。

线上有些业务架构是这样的:客户端===比;lv===比;Nginx===比;服务.LVS负载均衡方案采用博士模式,lv与Nginx配置统一贵宾。此时在客户端看来,只有一个IP地址,只有一个主持人。上述问题更为明显。

最后,运输也提供了配置DisableKeepAlives,禁用长连接,使用短连接访问第三方资源或者服务。


交通结构提供下面两个方法实现连接的获取与回收操作。

 func (t *运输)getConn (treq * transportRequest,厘米connectMethod) (pc * persistConn,犯错错误){}
  
  func (t *运输)tryPutIdleConn错误(pconn * persistConn) {} 

连接的获取主要分为两步走:1)尝试获取空闲连接;2)尝试新建连接:

选择{
  案例& lt; -w.ready:
  返回w。电脑,w.err//超时被取消
  案例& lt; -req.Cancel:
  返回nil, errRequestCanceledConn
  ……
  }
  
  var errRequestCanceledConn=errors.New (“;net/http:请求取消等待connection")//TODO: unify& # 63; 
 func (t *运输)queueForIdleConn (w * wantConn)(交付bool) {//如果配置了空闲超时时间,获取到连接需要检测,超时则关闭连接
  如果t。IdleConnTimeout祝辞0 {
  下来=time.Now阀门()(-t.IdleConnTimeout)
  }
  
  如果列表,好的:=t.idleConn [w.key];好{
  len(列表)的在0,,!停止{
  pconn:=列表(len(列表)1)
  tooOld:=! oldTime.IsZero (),,pconn.idleAt.Round (0) .Before(下来)//超时了,关闭连接
  如果tooOld {
  去pconn.closeConnIfStillIdle ()
  }//分发连接到wantConn
  交付=w。tryDeliver (pconn nil)
  }
  }//排队等待空闲连接
  问:=t.idleConnWait [w.key]
  q.pushBack (w)
  t.idleConnWait [w。关键]=问
  }