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