gpt4 book ai didi

http - 在 Go 中使用 DialContext 的性能问题

转载 作者:行者123 更新时间:2023-12-01 20:19:27 43 4
gpt4 key购买 nike

我使用 Go 的内置 http.Client 做了一个快速基准测试和 net .使用 DialContext 时会导致一些明显的性能问题与不使用时相反。

我基本上是想模仿我们公司的一个用例,其中 http.Client当完全用于相同的事情时,setup 的性能比默认配置低得多。我注意到评论 DialContext部分使它运行得更快。

基准测试只是打开一个线程池(示例中为 8 个)来创建到简单 URL 的连接,使用与线程数 (8) 相同大小的缓冲 channel 。

这是带有 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.154103ms):
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.154103ms

结果之间的差异在程序的几次运行中是一致的。

有没有人知道为什么会这样?

谢谢!

编辑:所以我尝试了 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 时有趣的发现vs 不使用它。

不使用 DialContext:
time taken to run 200 requests: 5.639808793s
new connections: 1
reused connections: 199

使用拨号上下文:
time taken to run 200 requests: 5.682882723s
new connections: 8
reused connections: 192

它更快......但为什么一个打开8个新连接,而另一个只打开1个?

最佳答案

获得如此大差异的唯一方法是,如果一种传输正在重用连接,而另一种则没有。为了确保您可以重用连接,您必须始终阅读响应正文。在某些情况下,连接可能会在不显式读取正文的情况下被重用,但它永远无法保证并且取决于许多事情,例如远程服务器关闭连接,响应是否已被 Transport 完全缓冲。 ,如果请求有上下文。

net/http/httptrace 包可以让您深入了解许多请求的内部结构,包括连接是否被重用。见 https://blog.golang.org/http-tracing举些例子。

设置 DisableKeepAlive将始终阻止连接被重用,使两者同样缓慢。总是像这样阅读响应将使两者同样快速:

req, _ := http.NewRequest(http.MethodGet, url, nil)
resp, err := httpClient.Do(req)
if err != nil {
// handle error
continue
}
io.Copy(ioutil.Discard, resp.Body)
resp.Body.Close()

如果您想在断开连接之前限制可以读取的数量,您可以简单地将主体包裹在 io.LimitedReader 中。

关于http - 在 Go 中使用 DialContext 的性能问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62071857/

43 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com