Skip to content

Commit 23b23e0

Browse files
committed
add post
1 parent ab7c92c commit 23b23e0

File tree

1 file changed

+116
-0
lines changed
  • content/posts/cannot assign requested address in golang http client

1 file changed

+116
-0
lines changed
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
---
2+
title: "Golang默认Http Client导致的cannot assign requested address错误"
3+
date: 2025-04-29T20:20:29+08:00
4+
draft: false
5+
tags:
6+
- golang
7+
---
8+
9+
### 问题表现
10+
11+
重现代码:
12+
13+
```
14+
package main
15+
16+
import (
17+
"fmt"
18+
"io"
19+
"net/http"
20+
"time"
21+
)
22+
23+
func main() {
24+
25+
client := &http.Client{
26+
Timeout: time.Duration(3) * time.Second,
27+
}
28+
29+
for i := 0; i < 100; i++ {
30+
go func() {
31+
for {
32+
req, _ := http.NewRequest(http.MethodGet, "https://baidu.com", nil)
33+
34+
rsp, err := client.Do(req)
35+
if err != nil {
36+
fmt.Println("request failed", err)
37+
continue
38+
}
39+
40+
rsp.Body.Close()
41+
42+
body, err := io.ReadAll(rsp.Body)
43+
if err != nil {
44+
fmt.Println("read body failed", err)
45+
continue
46+
}
47+
48+
fmt.Println(string(body))
49+
}
50+
}()
51+
}
52+
53+
select {}
54+
}
55+
```
56+
57+
启动后,随着请求越来越多,很快就出现了"cannot assign requested address"错误,服务器出现大量TIME_WAIT连接。
58+
59+
### 问题原因
60+
61+
62+
[net/http代码](https://cs.opensource.google/go/go/+/refs/tags/go1.24.2:src/net/http/client.go;l=61)
63+
64+
```
65+
type Client struct {
66+
// Transport specifies the mechanism by which individual
67+
// HTTP requests are made.
68+
// If nil, DefaultTransport is used.
69+
Transport RoundTripper
70+
```
71+
72+
未配置Transport时,使用默认的DefaultTransport。
73+
74+
```
75+
var DefaultTransport RoundTripper = &Transport{
76+
Proxy: ProxyFromEnvironment,
77+
DialContext: defaultTransportDialContext(&net.Dialer{
78+
Timeout: 30 * time.Second,
79+
KeepAlive: 30 * time.Second,
80+
}),
81+
ForceAttemptHTTP2: true,
82+
MaxIdleConns: 100,
83+
IdleConnTimeout: 90 * time.Second,
84+
TLSHandshakeTimeout: 10 * time.Second,
85+
ExpectContinueTimeout: 1 * time.Second,
86+
}
87+
```
88+
89+
这里指定了最大空闲连接未100,未指定单个host的最大空闲连接。
90+
91+
```
92+
func (t *Transport) maxIdleConnsPerHost() int {
93+
if v := t.MaxIdleConnsPerHost; v != 0 {
94+
return v
95+
}
96+
return DefaultMaxIdleConnsPerHost
97+
}
98+
```
99+
100+
如果未配置MaxIdleConnsPerHost,则使用默认的DefaultMaxIdleConnsPerHost配置。
101+
102+
```
103+
// DefaultMaxIdleConnsPerHost is the default value of [Transport]'s
104+
// MaxIdleConnsPerHost.
105+
const DefaultMaxIdleConnsPerHost = 2
106+
```
107+
108+
这下清楚了:
109+
110+
100个协程,请求同一个地址,只能保留2个空闲连接,超出的请求完就会退出,产生一个TIME_WAIT;
111+
112+
然后再创建一个连接,请求完关闭,又产生一个TIME_WAIT,直至耗尽端口。
113+
114+
### 解决方案
115+
116+
创建Http.Client时,配置MaxIdleConnsPerHost即可。

0 commit comments

Comments
 (0)