Я провел быстрый тест, используя встроенные Go http.Client
и net
. Это привело к заметным проблемам с производительностью при использовании DialContext
, а не при его неиспользовании.
Я в основном пытаюсь имитировать вариант использования, который есть в моей компании, где эта настройка http.Client
намного менее эффективна, чем конфигурация по умолчанию, когда используется точно для тех же вещей. И я заметил, что комментирование части DialContext
сделало его go быстрее.
Тест просто открывает пул потоков (8 в примере) для создания подключений к простому URL-адресу, используя буферизованный канал тот же размер, что и количество потоков (8).
Вот код с DialContext
(2.266333805s):
func main() {
var httpClient *http.Client
httpClient = &http.Client{
Transport: &http.Transport{
DialContext: (&net.Dialer{
Timeout: 3 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).DialContext,
},
}
url := "https://stackoverflow.com/"
wg := sync.WaitGroup{}
threads := 8
wg.Add(threads)
c := make(chan struct{}, threads)
start := time.Now()
for i := 0; i < threads; i++ {
go func() {
for range c {
req, _ := http.NewRequest(http.MethodGet, url, nil)
resp, err := httpClient.Do(req)
if err == nil {
resp.Body.Close()
}
}
wg.Done()
}()
}
for i := 0; i < 200; i++ {
c <- struct{}{}
}
close(c)
wg.Wait()
fmt.Println(time.Since(start))
}
Расчетное время было 2.266333805s
А вот код без DialContext
(731,154103мс):
func main() {
var httpClient *http.Client
httpClient = &http.Client{
Transport: &http.Transport{
},
}
url := "https://stackoverflow.com/"
wg := sync.WaitGroup{}
threads := 8
wg.Add(threads)
c := make(chan struct{}, threads)
start := time.Now()
for i := 0; i < threads; i++ {
go func() {
for range c {
req, _ := http.NewRequest(http.MethodGet, url, nil)
resp, err := httpClient.Do(req)
if err == nil {
resp.Body.Close()
}
}
wg.Done()
}()
}
for i := 0; i < 200; i++ {
c <- struct{}{}
}
close(c)
wg.Wait()
fmt.Println(time.Since(start))
}
Выведенное время было 731,154103мс
Разница между результаты были одинаковыми для нескольких запусков программы.
Кто-нибудь знает, почему это происходит?
Спасибо!
EDIT: Итак Я попробовал net/http/httptrace
и убедился, что основная часть ответа была полностью прочитана и закрыта:
go func() {
for range c {
req, _ := http.NewRequest(http.MethodGet, url, nil)
req = req.WithContext(httptrace.WithClientTrace(req.Context(), &httptrace.ClientTrace{
GotConn: t.gotConn,
}))
resp, _ := httpClient.Do(req)
ioutil.ReadAll(resp.Body)
resp.Body.Close()
}
wg.Done()
}()
Интересные выводы при использовании DialContext
и неиспользовании
НЕ ИСПОЛЬЗУЕТ DialContext:
time taken to run 200 requests: 5.639808793s
new connections: 1
reused connections: 199
ИСПОЛЬЗУЕТ DialContext:
time taken to run 200 requests: 5.682882723s
new connections: 8
reused connections: 192
Это f астра ... Но почему один открывает 8 новых подключений, а другой всего 1?