golang http鏈接池失效的幾種狀況及具體緣由分析

    首先,鏈接池失效,問題產生背景是高頻agent,agent 會發起大量的http 請求,可是,本想net/http 是支持長鏈接的,可是,幾種狀況,都產生了大量的time_wait,這裏予以總結。html

該文章後續仍在不斷的更新修改中, 請移步到原文地址http://dmwan.ccgolang

    第一種狀況是誤用transport ,爲了設置代理,爲每一個請求,都new 了一個transport 。web

client := &http.Client{

     CheckRedirect: redirectPolicyFunc,
     Timeout: time.Duration(10)*time.Second,//設置超時

}

client.Transport = &http.Transport{
     Proxy: http.ProxyURL(proxyUrl),
} //設置代理ip

    失效的緣由,是client 是線程安全的,golang鏈接池的維度是transport, 在transport 裏面維護了兩個map,暫存鏈接。安全

    第二種狀況是沒設置 MaxIdleConnsPerHost, 和鏈接的timeout, 一旦高頻的鏈接超過MaxIdleConnsPerHost 的數目,同時超過超時,鏈接就會釋放。正確的設置是實例化transport 的時候,評估好 connsPerHost, 以下:socket

var DefaultTransport RoundTripper = &Transport{
        ... 
  MaxIdleConnsPerHost: 1000,
  IdleConnTimeout:       90 * time.Second,
        ... 
}

    第三種狀況是resp.body 忘了讀取,直接致使新請求會直接新建鏈接。其實能夠理解,沒read body 的socket, 若是直接複用,會產生什麼樣後果?全部使用這個套接字的鏈接都會錯亂。 示例以下,線程

package main

import (
  "fmt"
  "html"
  "log"
  "net"
  "net/http"
  "time"
)

func startWebserver() {

  http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
      fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
  })

  go http.ListenAndServe(":8080", nil)

}

func startLoadTest() {
  count := 0
  for {
      resp, err := http.Get("http://localhost:8080/")
      if err != nil {
          panic(fmt.Sprintf("Got error: %v", err))
      }
      resp.Body.Close()
      log.Printf("Finished GET request #%v", count)
      count += 1
  }

}

func main() {

  // start a webserver in a goroutine
  startWebserver()

  startLoadTest()

}

    這裏能夠使用ss -s 查看鏈接數,若是不關心返回body ,能夠直接丟棄代理

io.Copy(ioutil.Discard, resp.Body)  //Discard 是一個 io.Writer,對它進行的任何 Write 調用都將無條件成功
相關文章
相關標籤/搜索