校园春色亚洲色图_亚洲视频分类_中文字幕精品一区二区精品_麻豆一区区三区四区产品精品蜜桃

主頁 > 知識庫 > Golang你一定要懂的連接池實現

Golang你一定要懂的連接池實現

熱門標簽:賺地圖標注的錢犯法嗎 長沙ai機器人電銷 濮陽自動外呼系統代理 福州鐵通自動外呼系統 地圖標注測試 澳門防封電銷卡 烏魯木齊人工電銷機器人系統 智能電銷機器人營銷 廣東語音外呼系統供應商

問題引入

作為一名Golang開發者,線上環境遇到過好幾次連接數暴增問題(mysql/redis/kafka等)。

糾其原因,Golang作為常駐進程,請求第三方服務或者資源完畢后,需要手動關閉連接,否則連接會一直存在。而很多時候,開發者不一定記得關閉這個連接。

這樣是不是很麻煩?于是有了連接池。顧名思義,連接池就是管理連接的;我們從連接池獲取連接,請求完畢后再將連接還給連接池;連接池幫我們做了連接的建立、復用以及回收工作。

在設計與實現連接池時,我們通常需要考慮以下幾個問題:

  • 連接池的連接數目是否有限制,最大可以建立多少個連接?
  • 當連接長時間沒有使用,需要回收該連接嗎?
  • 業務請求需要獲取連接時,此時若連接池無空閑連接且無法新建連接,業務需要排隊等待嗎?
  • 排隊的話又存在另外的問題,隊列長度有無限制,排隊時間呢?

Golang連接池實現原理

我們以Golang HTTP連接池為例,分析連接池的實現原理。

結構體Transport

Transport結構定義如下:

type Transport struct {
  //操作空閑連接需要獲取鎖
  idleMu    sync.Mutex
  //空閑連接池,key為協議目標地址等組合
  idleConn   map[connectMethodKey][]*persistConn // most recently used at end
  //等待空閑連接的隊列,基于切片實現,隊列大小無限制
  idleConnWait map[connectMethodKey]wantConnQueue // waiting getConns
  
  //排隊等待建立連接需要獲取鎖
  connsPerHostMu  sync.Mutex
  //每個host建立的連接數
  connsPerHost   map[connectMethodKey]int
  //等待建立連接的隊列,同樣基于切片實現,隊列大小無限制
  connsPerHostWait map[connectMethodKey]wantConnQueue // waiting getConns
  
  //最大空閑連接數
  MaxIdleConns int
  //每個目標host最大空閑連接數;默認為2(注意默認值)
  MaxIdleConnsPerHost int
  //每個host可建立的最大連接數
  MaxConnsPerHost int
  //連接多少時間沒有使用則被關閉
  IdleConnTimeout time.Duration
  
  //禁用長連接,使用短連接
  DisableKeepAlives bool
}

可以看到,連接護著隊列,都是一個map結構,而key為協議目標地址等組合,即同一種協議與同一個目標host可建立的連接或者空閑連接是有限制的。

需要特別注意的是,MaxIdleConnsPerHost默認等于2,即與目標主機最多只維護兩個空閑連接。這會導致什么呢?

如果遇到突發流量,瞬間建立大量連接,但是回收連接時,由于最大空閑連接數的限制,該聯機不能進入空閑連接池,只能直接關閉。結果是,一直新建大量連接,又關閉大量連,業務機器的TIME_WAIT連接數隨之突增。

線上有些業務架構是這樣的:客戶端 ===> LVS ===> Nginx ===> 服務。LVS負載均衡方案采用DR模式,LVS與Nginx配置統一VIP。此時在客戶端看來,只有一個IP地址,只有一個Host。上述問題更為明顯。

最后,Transport也提供了配置DisableKeepAlives,禁用長連接,使用短連接訪問第三方資源或者服務。

連接獲取與回收

Transport結構提供下面兩個方法實現連接的獲取與回收操作。

func (t *Transport) getConn(treq *transportRequest, cm connectMethod) (pc *persistConn, err error) {}

func (t *Transport) tryPutIdleConn(pconn *persistConn) error {}

連接的獲取主要分為兩步走:1)嘗試獲取空閑連接;2)嘗試新建連接:

//getConn方法內部實現

if delivered := t.queueForIdleConn(w); delivered {
  return pc, nil
}
  
t.queueForDial(w)

當然,可能獲取不到連接而需要排隊,此時怎么辦呢?當前會阻塞當前協程了,直到獲取連接為止,或者httpclient超時取消請求:

select {
  case -w.ready:
    return w.pc, w.err
    
  //超時被取消
  case -req.Cancel:
    return nil, errRequestCanceledConn
  ……
}

var errRequestCanceledConn = errors.New("net/http: request canceled while waiting for connection") // TODO: unify?

排隊等待空閑連接的邏輯如下:

func (t *Transport) queueForIdleConn(w *wantConn) (delivered bool) {
  //如果配置了空閑超時時間,獲取到連接需要檢測,超時則關閉連接
  if t.IdleConnTimeout > 0 {
    oldTime = time.Now().Add(-t.IdleConnTimeout)
  }
  
  if list, ok := t.idleConn[w.key]; ok {
    for len(list) > 0  !stop {
      pconn := list[len(list)-1]
      tooOld := !oldTime.IsZero()  pconn.idleAt.Round(0).Before(oldTime)
      //超時了,關閉連接
      if tooOld {
        go pconn.closeConnIfStillIdle()
      }
      
      //分發連接到wantConn
      delivered = w.tryDeliver(pconn, nil)
    }
  }
  
  //排隊等待空閑連接
  q := t.idleConnWait[w.key]
  q.pushBack(w)
  t.idleConnWait[w.key] = q
}

排隊等待新建連接的邏輯如下:

func (t *Transport) queueForDial(w *wantConn) {
  //如果沒有限制最大連接數,直接建立連接
  if t.MaxConnsPerHost = 0 {
    go t.dialConnFor(w)
    return
  }
  
  //如果沒超過連接數限制,直接建立連接
  if n := t.connsPerHost[w.key]; n  t.MaxConnsPerHost {
    go t.dialConnFor(w)
    return
  }
  
  //排隊等待連接建立
  q := t.connsPerHostWait[w.key]
  q.pushBack(w)
  t.connsPerHostWait[w.key] = q
}

連接建立完成后,同樣會調用tryDeliver分發連接到wantConn,同時關閉通道w.ready,這樣主協程糾接觸阻塞了。

func (w *wantConn) tryDeliver(pc *persistConn, err error) bool {
  w.pc = pc
  close(w.ready)
}

請求處理完成后,通過tryPutIdleConn將連接放回連接池;這時候如果存在等待空閑連接的協程,則需要分發復用該連接。另外,在回收連接時,還需要校驗空閑連接數目是否超過限制:

func (t *Transport) tryPutIdleConn(pconn *persistConn) error {
  //禁用長連接;或者最大空閑連接數不合法
  if t.DisableKeepAlives || t.MaxIdleConnsPerHost  0 {
    return errKeepAlivesDisabled
  }
  
  if q, ok := t.idleConnWait[key]; ok {
    //如果等待隊列不為空,分發連接
    for q.len() > 0 {
      w := q.popFront()
      if w.tryDeliver(pconn, nil) {
        done = true
        break
      }
    }
  }
  
  //空閑連接數目超過限制,默認為DefaultMaxIdleConnsPerHost=2
  idles := t.idleConn[key]
  if len(idles) >= t.maxIdleConnsPerHost() {
    return errTooManyIdleHost
  }

}

空閑連接超時關閉

Golang HTTP連接池如何實現空閑連接的超時關閉邏輯呢?從上述queueForIdleConn邏輯可以看到,每次在獲取到空閑連接時,都會檢測是否已經超時,超時則關閉連接。

那如果沒有業務請求到達,一直不需要獲取連接,空閑連接就不會超時關閉嗎?其實在將空閑連接添加到連接池時,Golang同時還設置了定時器,定時器到期后,自然會關閉該連接。

pconn.idleTimer = time.AfterFunc(t.IdleConnTimeout, pconn.closeConnIfStillIdle)

排隊隊列怎么實現

怎么實現隊列模型呢?很簡單,可以基于切片:

queue  []*wantConn

//入隊
queue = append(queue, w)

//出隊
v := queue[0]
queue[0] = nil
queue = 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 {
  if q.headPos >= len(q.head) {
    if len(q.tail) == 0 {
      return nil
    }
    // Pick up tail as new head, clear tail.
    q.head, q.headPos, q.tail = q.tail, 0, q.head[:0]
  }
  w := q.head[q.headPos]
  q.head[q.headPos] = nil
  q.headPos++
  return w
}

到此這篇關于Golang你一定要懂的連接池實現的文章就介紹到這了,更多相關Golang 連接池內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:
  • Golang連接池的幾種實現案例小結
  • golang sql連接池的實現方法詳解
  • golang實現基于channel的通用連接池詳解

標簽:調研邀請 貴陽 西雙版納 太原 慶陽 阿克蘇 德州 廣西

巨人網絡通訊聲明:本文標題《Golang你一定要懂的連接池實現》,本文關鍵詞  Golang,你,一,定要,懂的,連接,;如發現本文內容存在版權問題,煩請提供相關信息告之我們,我們將及時溝通與處理。本站內容系統采集于網絡,涉及言論、版權與本站無關。
  • 相關文章
  • 下面列出與本文章《Golang你一定要懂的連接池實現》相關的同類信息!
  • 本頁收集關于Golang你一定要懂的連接池實現的相關信息資訊供網民參考!
  • 推薦文章
    校园春色亚洲色图_亚洲视频分类_中文字幕精品一区二区精品_麻豆一区区三区四区产品精品蜜桃
    国产精品久久久久久久久免费相片 | 欧美性感一类影片在线播放| 在线一区二区三区四区| 欧美tickling网站挠脚心| 亚洲欧美自拍偷拍| 精品影视av免费| 欧美性猛交一区二区三区精品| 国产日韩欧美不卡在线| 美腿丝袜在线亚洲一区| 91高清视频免费看| 综合激情成人伊人| 国产一二精品视频| 日韩精品专区在线影院观看| 午夜精品久久久久久久久久久 | 国内精品伊人久久久久影院对白| 91国产免费观看| 亚洲情趣在线观看| 播五月开心婷婷综合| 精品免费视频.| 秋霞av亚洲一区二区三| 欧美日韩国产成人在线91| 一二三区精品福利视频| 色婷婷综合久久久久中文一区二区 | 亚洲精品国产第一综合99久久 | 7777精品伊人久久久大香线蕉经典版下载| 国产精品乱人伦一区二区| 国产在线看一区| 精品久久一二三区| 久久成人免费网站| 91精品国产一区二区| 日本欧美久久久久免费播放网| 欧美午夜一区二区| 亚洲第一久久影院| 制服丝袜日韩国产| 美女诱惑一区二区| 久久夜色精品一区| 国产一区日韩二区欧美三区| 久久精品一区二区三区不卡牛牛| 国产一区亚洲一区| 国产欧美日韩麻豆91| 成人教育av在线| 中文字幕在线不卡视频| 91精彩视频在线| 人人狠狠综合久久亚洲| 日韩免费看的电影| 国产福利一区二区三区在线视频| 国产三级欧美三级日产三级99 | 国产在线播放一区三区四| 日韩一区二区三区视频| 久久99蜜桃精品| 国产精品午夜春色av| 日本韩国欧美一区| 石原莉奈在线亚洲二区| 日韩欧美在线观看一区二区三区| 国产一区二区伦理片| 国产欧美一区二区三区在线看蜜臀 | 国产一区二区看久久| 国产精品久久久久影院老司| 欧美偷拍一区二区| 久久激情综合网| 亚洲视频小说图片| 欧美顶级少妇做爰| 成人黄色777网| 午夜av区久久| 中文字幕精品综合| 91精品国产乱码久久蜜臀| 国产成人99久久亚洲综合精品| 亚洲你懂的在线视频| 欧美va日韩va| 欧洲另类一二三四区| 九九久久精品视频| 亚洲激情在线激情| 久久香蕉国产线看观看99| 91福利在线导航| 国产一区二区三区综合| 亚洲一卡二卡三卡四卡| 国产欧美一区二区精品性| 欧美性猛交xxxxxx富婆| 国产成人免费视频| 亚洲夂夂婷婷色拍ww47| 中文字幕欧美区| 欧美一区二区国产| 欧美在线一区二区三区| 国产成人99久久亚洲综合精品| 亚洲影院久久精品| 成人免费一区二区三区在线观看| 日韩一区二区麻豆国产| 欧美色精品天天在线观看视频| 国产高清无密码一区二区三区| 午夜精品在线视频一区| 一区二区三区在线不卡| 国产欧美日韩一区二区三区在线观看 | 日韩av在线播放中文字幕| 亚洲综合色丁香婷婷六月图片| 久久久综合视频| 日韩欧美一卡二卡| 欧美人体做爰大胆视频| 一本久道久久综合中文字幕| 不卡一区二区三区四区| 国产精品1区2区3区在线观看| 日本午夜精品视频在线观看| 亚洲一区二区三区小说| 亚洲女同女同女同女同女同69| 国产亚洲欧洲997久久综合| 日韩一区二区免费高清| 51精品秘密在线观看| 欧美乱熟臀69xxxxxx| 欧美伊人久久久久久久久影院| www.欧美日韩国产在线| av在线一区二区| 不卡的av电影| 99亚偷拍自图区亚洲| 国产aⅴ综合色| 风流少妇一区二区| 成人免费视频网站在线观看| 风间由美性色一区二区三区| 成人性生交大合| av电影天堂一区二区在线观看| 成人免费高清在线| 91免费观看视频| 欧美日韩三级在线| 777色狠狠一区二区三区| 91精品国产色综合久久不卡蜜臀 | 老司机精品视频线观看86| 久久国产夜色精品鲁鲁99| 国产一区二区三区黄视频 | 欧美精品日韩精品| 91精品国产黑色紧身裤美女| 欧美r级电影在线观看| 欧美精品一区二区三区在线播放 | 国产91精品一区二区麻豆网站| 国产精品亚洲视频| 色综合久久久久久久久久久| 欧美丝袜丝交足nylons图片| 欧美一区二区三区四区视频| 欧美成人性战久久| 国产精品免费av| 亚洲成av人影院| 国内国产精品久久| 色又黄又爽网站www久久| 91精品国产高清一区二区三区 | 欧美白人最猛性xxxxx69交| 国产日韩欧美精品综合| 一区二区三区欧美在线观看| 免费在线成人网| 成a人片国产精品| 在线播放中文字幕一区| 久久久噜噜噜久噜久久综合| 亚洲色图欧洲色图婷婷| 日韩中文字幕一区二区三区| 国产一区二区调教| 欧美亚洲免费在线一区| 久久精品视频在线看| 亚洲一区二区视频在线观看| 久久99国产精品免费| 日韩欧美中文字幕一区| 国产日韩亚洲欧美综合| 五月天丁香久久| www.亚洲在线| 欧美成人性战久久| 亚洲一区二区在线免费观看视频| 国产乱码精品一区二区三区av| 色哟哟日韩精品| 久久精品一区二区| 日韩av一二三| 91视频一区二区三区| 久久影院视频免费| 日韩国产在线一| 在线看国产一区| 日韩久久一区二区| 国产麻豆成人精品| 日韩一区二区电影| 一区二区三区在线观看动漫| 成人小视频在线| 久久久久久免费网| 免费在线视频一区| 欧美日韩国产一区| 亚洲一区二区黄色| aaa亚洲精品| 国产精品天天看| 国产精品自在欧美一区| 91精品国产91久久综合桃花 | 美国一区二区三区在线播放| 欧美羞羞免费网站| 亚洲综合色噜噜狠狠| 99麻豆久久久国产精品免费| 国产婷婷色一区二区三区四区| 麻豆精品国产传媒mv男同| 欧美一区二区三区男人的天堂| 亚洲成a人片在线观看中文| 日本高清成人免费播放| 亚洲人成网站色在线观看| 风间由美一区二区av101 | 亚洲午夜私人影院| 99久久99久久久精品齐齐| 国产精品三级av| 99免费精品视频| 亚洲精品中文在线影院| 在线免费不卡视频| 亚洲电影中文字幕在线观看|