Gopher必讀:HttpClient的兩個坑位

部落格猿馬甲哥發表於2022-03-02

http是我們最常見的客戶端/服務端傳輸協議,在golang中,預設的net/http包有一些坑位,需要調整以獲得更加效能。

在golang程式中,我也遇到因為不合理使用 http client導致的程式崩潰問題。

坑:1:預設的HttpClient

預設的HttpClient不包含請求超時時間,如果你使用http.Get(url)或者&Client{}, 這將會使用http.DefaultClient,這個結構體內no timeout

假如發出請求的服務端API有問題:沒有及時響應httpclient請求但是保持了連線, 在高併發情況下,開啟的連線數會持續增長,最終導致客戶端伺服器資源到達瓶頸。

解決方案:不要使用預設的HTTPClient, 總是為HttpClient指定Timeout

	client := &http.Client{
		Timeout: 10 * time.Second,
	}

HttpClient Timeout包括連線、重定向(如果有)、從Response Body讀取的時間,內建定時器會在Get,Head、Post、Do 方法之後繼續執行,直到讀取完Response.Body。

這裡有個有關HttpClient Timeout的排障問題,你可參考。


.NET HttpClient Timeout: The default value is 100,000 milliseconds (100 seconds).

坑2:預設的Http Transport

目前常見的HttpClient(.NET Core,golang) 都會有連線池的概念, 客戶端會盡量複用池中已經建立的連線。

go的Transport 可理解為連線池中的連線。

// DefaultTransport is the default implementation of Transport and is
// used by DefaultClient. It establishes network connections as needed
// and caches them for reuse by subsequent calls. It uses HTTP proxies
// as directed by the $HTTP_PROXY and $NO_PROXY (or $http_proxy and
// $no_proxy) environment variables.
var DefaultTransport RoundTripper = &Transport{
	Proxy: ProxyFromEnvironment,
	DialContext: (&net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
	}).DialContext,
	ForceAttemptHTTP2:     true,
	MaxIdleConns:          100,
	IdleConnTimeout:       90 * time.Second,
	TLSHandshakeTimeout:   10 * time.Second,
	ExpectContinueTimeout: 1 * time.Second,
}


// DefaultMaxIdleConnsPerHost is the default value of Transport's
// MaxIdleConnsPerHost.
const DefaultMaxIdleConnsPerHost = 2

預設的

  • golang的Http Client連線池有100個連線,
  • 每個連線預設的空閒時間90s(90s內有請求過來,可以複用該連線)
  • 上面的KeepAlive是tcp探活的時間間隔,並不是我們HTTP連線複用的 Keep-Alive

有坑位的是DefaultMaxIdleConnsPerHost=2:每個主機(服務)保留的空閒連線數是2個。

這意味著,當首次針對某主機發出100個請求,這100個請求會同時利用連線池中的100個連線,之後因為這個限制,客戶端被迫主動關閉98個連線,此時客戶端機器會出現98 個time_wait(time_Wait會存在2MSL,大概2min,佔用了機器資源),

新的請求被迫新開連線,然後立馬主動關閉,只維持2個複用連線, 最後造成客戶端機器存在大量time_Wait

從我司實際專案看,造成CPU高漲,並且無法新開連線。

解決方案:不要使用預設Transport,增加MaxIdleConnsPerHost


本人回顧了.NET HttpClient,貌似不用刻意關閉這個值。

實際上,.NET也存在這個MaxIdleConnectionPerServer配置,但是.NET Core這個PerServer被設定為int.maxvalue,所以我們無需關注,.NET真香。

我的收穫

通過本文,我們談到了golang HttpClient的2個坑位、由坑位導致的現象和排障思路,各位看官,有則改之無則加勉。

並且我們對比了.NET Core語言中HttpClient的預設配置,各位看官心中有數。

相關文章