代码
package main
import (
"crypto/tls"
"io/ioutil"
"log"
"net/http"
"os"
"path"
"strings"
"time"
)
func panicIfNotNil(e error) {
if e != nil {
panic(e)
}
}
func loop() {
ex, err := os.Executable()
panicIfNotNil(err)
ex = path.Dir(ex)
for {
now := time.Now()
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
DisableKeepAlives: true, // 可以关闭链接
}
req, err := http.NewRequest("GET", "https://httpbin.org/ip", nil)
panicIfNotNil(err)
client := &http.Client{
Timeout: time.Second * 8,
Transport: tr,
}
resp, err := client.Do(req)
if resp != nil {
bodyByteArr, err := ioutil.ReadAll(resp.Body)
panicIfNotNil(err)
log.Printf("status_code:%d, body:%s, cost:%v", resp.StatusCode, string(bodyByteArr), time.Since(now))
defer resp.Body.Close()
}
if err != nil && strings.Index(err.Error(), "Client.Timeout exceeded while awaiting headers") > -1 {
continue
}
panicIfNotNil(err)
time.Sleep(time.Second * 3)
}
}
func main() {
loop()
}
一开始只有 4-m 的样子,过了两分钟就到 10M 左右了,这是为什么呀,我可能确保链接时关闭了的 求大佬指点下
1
jworg 2021-03-06 22:16:37 +08:00
|
2
jinliming2 2021-03-06 22:27:12 +08:00 1
defer resp.Body.Close() 只会在 func 推出后执行,你这里一直在死循环,函数不推出,resp.Body.Close() 永远不会执行。
你这里直接把 defer 去掉应该就好了。 |
3
Mohanson 2021-03-06 22:29:01 +08:00 via Android 1
for 循环里面 defer,楼上正解
|
4
chenqh OP @jinliming2 去掉了 defer 内存也在涨
|
6
chenqh OP [vagrant@localhost simple]$ go version
go version go1.15.3 linux/amd64 |
7
jinliming2 2021-03-06 22:53:28 +08:00
emmmm,看上去应该没有其他泄露的地方了,你 keepAlive 关了,应该不用手动 CloseIdleConnections 了。
建议你再观察观察,因为你这个是一直在循环,你多观察一会,它是会一直增长,还是会停留在一定程度? 如果涨到一定程度就不涨了,那可能就跟 go 的垃圾回收机制有关了。 |
8
chenqh OP @jinliming2 现在已经 11m 了
``` Every 2.0s: ~/soft/pstat --name="simple" --exclude="pstat" Sat Mar 6 22:57:49 2021 name count mem open_files net_connections ---- ----- --- ---------- --------------- simple 1 11.03m 20 0 ``` |
9
chenqh OP @jinliming2 难道大佬们都是全局 client 的吗?
|
10
jworg 2021-03-06 23:04:29 +08:00
我还是直接把 Transport 中注释贴出来吧,你看看你创建了多少个,正常应该 reused 的
// Transport is an implementation of RoundTripper that supports HTTP, // HTTPS, and HTTP proxies (for either HTTP or HTTPS with CONNECT). // // By default, Transport caches connections for future re-use. // This may leave many open connections when accessing many hosts. // This behavior can be managed using Transport's CloseIdleConnections method // and the MaxIdleConnsPerHost and DisableKeepAlives fields. // // Transports should be reused instead of created as needed. // Transports are safe for concurrent use by multiple goroutines. // // A Transport is a low-level primitive for making HTTP and HTTPS requests. // For high-level functionality, such as cookies and redirects, see Client. |
11
chenqh OP @jworg
``` [vagrant@localhost simple]$ sudo lsof -p 9575 -a -i 4 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME simple 9575 vagrant 6u IPv4 6958222 0t0 TCP localhost.localdomain:38460->ec2-34-231-30-52.compute-1.amazonaws.com:https (ESTABLISHED) ``` 只有一个 |
12
chenqh OP 现在好像卡在 11.39 不动了
``` Every 2.0s: ~/soft/pstat --name="simple" --exclude="pstat" Sat Mar 6 23:50:46 2021 name count mem open_files net_connections ---- ----- --- ---------- --------------- simple 1 11.39m 20 0 ``` 明天在看一下 |
13
jworg 2021-03-06 23:58:13 +08:00
@chenqh 我之前回复弄错了,没有仔细看代码。我这边是在 11M 到 12M 波动,应该如 7 楼所说。另外建议安装代码检查工具,可以提示那个 defer 以及 resp.Body.Close()的返回值的检查。
|
14
Immortal 2021-03-07 00:01:27 +08:00
pprof 打开观察下就好了 何必在这里猜
代码看起来没啥问题 defer 的问题前面也有人说了 |
15
nanmu42 2021-03-08 08:07:57 +08:00 via Android
你再测久一些试试看,12M 还太少了。
比如 10 个小时后到了 100M,那才是真的漏了。 Go 1.16 之前,不用的内存还给操作系统的时机都比较延后,你也可以更新版本后试试: https://github.com/golang/go/issues/42330 |
16
binbinyouliiii 2021-03-08 09:50:13 +08:00
Go 不是带 GC 的吗?没触发 GC ?
|
17
xkeyideal 2021-03-08 09:57:17 +08:00 1
go 1.15 版本的内存回收机制导致的,升级至 1.16 版本试试吧
另外 defer 的问题,每次 new http.Client 的方式也是错的 |
18
jitongxi 2021-03-08 10:25:52 +08:00
是这样的, 只要你用了 go 的标准 http 库,内存就会一直多 5m 左右,应该有很多初始化的上下文, 估计要看源码才知道
|
19
cheng6563 2021-03-08 10:49:14 +08:00
@binbinyouliiii GC 了也不会把内存还给操作系统,1.16 之前尤其如此
|
20
DollarKiller 2021-03-08 11:05:19 +08:00
是长链接问题,你看下源码就知道了, 外婆卖瓜下 github.com/dollarkillerx/urllib 这个库有效的解决了这个问题
|
21
xx6412223 2021-03-08 16:25:27 +08:00
开一夜试试。我感觉问题不大
|