车驰夜幕|Gopher 你一定要懂的连接池,作为( 二 )


func(t*Transport)queueForDial(w*wantConn){//如果没有限制最大连接数 , 直接建立连接ift.MaxConnsPerHost<=0{got.dialConnFor(w)return}//如果没超过连接数限制 , 直接建立连接ifn:=t.connsPerHost[w.key];n连接建立完成后 , 同样会调用tryDeliver分发连接到wantConn , 同时关闭通道w.ready , 这样主协程纠接触阻塞了 。
func(w*wantConn)tryDeliver(pc*persistConn,errerror)bool{w.pc=pcclose(w.ready)}请求处理完成后 , 通过tryPutIdleConn将连接放回连接池;这时候如果存在等待空闲连接的协程 , 则需要分发复用该连接 。 另外 , 在回收连接时 , 还需要校验空闲连接数目是否超过限制:
func(t*Transport)tryPutIdleConn(pconn*persistConn)error{//禁用长连接;或者最大空闲连接数不合法ift.DisableKeepAlives||t.MaxIdleConnsPerHost0{w:=q.popFront()ifw.tryDeliver(pconn,nil){done=truebreak}}}//空闲连接数目超过限制 , 默认为DefaultMaxIdleConnsPerHost=2idles:=t.idleConn[key]iflen(idles)>=t.maxIdleConnsPerHost(){returnerrTooManyIdleHost}}空闲连接超时关闭GolangHTTP连接池如何实现空闲连接的超时关闭逻辑呢?从上述queueForIdleConn逻辑可以看到 , 每次在获取到空闲连接时 , 都会检测是否已经超时 , 超时则关闭连接 。
那如果没有业务请求到达 , 一直不需要获取连接 , 空闲连接就不会超时关闭吗?其实在将空闲连接添加到连接池时 , Golang同时还设置了定时器 , 定时器到期后 , 自然会关闭该连接 。
pconn.idleTimer=time.AfterFunc(t.IdleConnTimeout,pconn.closeConnIfStillIdle)排队队列怎么实现怎么实现队列模型呢?很简单 , 可以基于切片:
queue[]*wantConn//入队queue=append(queue,w)//出队v:=queue[0]queue[0]=nilqueue=queue[1:]这样有什么问题吗?随着频繁的入队与出队操作 , 切片queue的底层数组 , 会有大量空间无法复用而造成浪费 。 除非该切片执行了扩容操作 。
Golang在实现队列时 , 使用了两个切片head和tail;head切片用于出队操作 , tail切片用于入队操作;出队时 , 如果head切片为空 , 则交换head与tail 。 通过这种方式 , Golang实现了底层数组空间的复用 。
func(q*wantConnQueue)pushBack(w*wantConn){q.tail=append(q.tail,w)}func(q*wantConnQueue)popFront()*wantConn{ifq.headPos>=len(q.head){iflen(q.tail)==0{returnnil}//Pickuptailasnewhead,cleartail.q.head,q.headPos,q.tail=q.tail,0,q.head[:0]}w:=q.head[q.headPos]q.head[q.headPos]=nilq.headPos++returnw}【车驰夜幕|Gopher 你一定要懂的连接池,作为】本文作者:源代码
原文链接: