Gopher必讀:HttpClient的兩個坑位

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的默認配置,各位看官心中有數。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章