Golang Go语言中反向代理出错后,会出现线程泄露?
Golang Go语言中反向代理出错后,会出现线程泄露?
我用 GO 写了一个很简单的反向代理程序,代码如下:
func main() {
go func() {
log.Fatal( http.ListenAndServe(":5050", nil))
}()
link, err := url.Parse("http://127.0.0.1:8908")
if err != nil {
log.Fatal(err)
}
log.Fatal( http.ListenAndServe(":10086", httputil.NewSingleHostReverseProxy(link)))
}
端口 8908 后边是一个 NodeJS socket.io 服务端,现在我通过 NodeJS 向 10086 端口同时发起 1000 个 websocket 连接,这些链接均会被代理到 8908 端口服务,然后将所有连接断开,重复几轮后可见控制台报如下错误:
2022/01/18 14:06:54 http: proxy error: dial tcp 127.0.0.1:8908: connectex: Only one usage of each socket address (protocol/network address/port) is normally permitted.
此时通过 windows 资源监视器可看到反向代理程序线程数为 298 ,通过 Process Explorer 可以看到 Handles 数为 1855 ,并且存在大量 Thread 。
再次建立连接再断开,反向代理程序的线程数持续增长,通过 pprof 可见 threadcreate 此时达到了惊人的 472 ,并一直保持。
似乎是系统端口数量限制导致的错误,然后错误导致线程未被回收?
我是 GO 新手,大家有遇到过这个问题吗?
更多关于Golang Go语言中反向代理出错后,会出现线程泄露?的实战教程也可以访问 https://www.itying.com/category-94-b0.html
不是你断开,反代和 8908 之间的连接就马上断开,和你系统的 TCP 的 TIME_WAIT 有关
参考 https://help.socketlabs.com/docs/how-to-fix-error-only-one-usage-of-each-socket-address-protocolnetwork-addressport-is-normally-permitted
更多关于Golang Go语言中反向代理出错后,会出现线程泄露?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
感谢回复
目前我已将系统 TCP 释放时间( TcpTimedWaitDelay )调整为了 30 秒,并重新进行了测试。
几轮 websocket 连接-断开 之后,通过资源监视器看到反代程序积累了 465 个线程,此时 websoket 客户端处于断开的状态(进程已退出),8908 websocket 服务端正常运行,等待了 10 分钟线程数并未减少。紧接着关闭 8908 服务(退出进程),等待 10 分钟,线程数依然保持不变。
有条件的话可以测试下 caddy 反代是不是这样?
官方库对这部分常用场景应该有考虑才对…现在没有头绪,待学习别人反代 websocket 之后再来更新。
好的,感谢提醒
在 windows 10 powershell 通过以下命令启动 caddy <br># caddy_windows_amd64.exe reverse-proxy --from :10086 --to 127.0.0.1:8908<br>
也会积累线程,奇怪了。
有时间再测测最多能积累多少线程,会不会导致进程崩溃的问题吧。
import 加一行
_ "net/http/pprof"
跑起来看看这个有没什么发现
http://127.0.0.1:5050/debug/pprof/
测试大量短链接端口不应该在 windows 测试。应该在 linux 测试 ,windows 本身就对 tcp 数量有限制 。其次就是 TCP TIME_WAIT ,不过 windows 的 TCP TIME_WAIT 也不知道怎么设
你那报错我也遇到。随便用 hey 测几轮并发 ,windows 马上就报端口不够用了。
功力太浅,看不太明白,只记得在所有连接断开后,goroutine 数目恢复到个位数,而 threadcreate 依然居高不下。
TCP TIME_WAIT 设置看这里: https://docs.microsoft.com/en-us/biztalk/technical-guides/settings-that-can-be-modified-to-improve-network-performance
另外我们生产服务器只有 windows server …
这样反向代理 WS 是不行的吧,你想 HTTP 握手后升级到 WS ,HTTP 客户端怎么和 WS 服务器通信呢?协议都不一样了~
https://github.com/yeqown/fasthttp-reverse-proxy/blob/master/ws_reverseproxy.go#L71 供参考
其次,看你的错误信息,我猜测你这个代理应该连握手都没办法完成吧~(协议问题),你测试结果有正确的响应吗?
ReverseProxy 模块中有 websocket 的实现,详情看 https://go.dev/src/net/http/httputil/reverseproxy.go:280,306 。
在Golang(通常简称为Go)中使用反向代理时,如果遇到错误处理不当,确实有可能导致资源泄露,包括线程(在Go中更准确地说是goroutine)泄露。不过,需要明确的是,Go的并发模型基于goroutine而非传统意义上的线程,因此这里提到的“线程泄露”实际上是goroutine泄露。
反向代理通常通过net/http/httputil
包中的ReverseProxy
结构体实现。如果在使用该代理时没有正确处理错误(例如,代理目标服务器响应错误或连接中断),且没有适当地关闭连接或取消正在进行的请求,那么这些goroutine可能会持续存在,占用系统资源。
要避免这种情况,可以采取以下措施:
- 设置超时:为HTTP客户端和服务器设置合理的超时时间,确保在请求超时后能够释放资源。
- 错误处理:确保所有错误都被捕获并妥善处理,包括在请求处理函数中捕获panic,避免未预期的崩溃导致资源未清理。
- 资源监控:使用Go的运行时工具(如
pprof
)监控goroutine的数量,及时发现并排查泄露问题。 - 使用
context
:通过context
包管理请求的生命周期,确保在请求取消或超时时,所有相关资源都能被正确释放。
总之,虽然Go语言的设计有助于减少资源泄露的风险,但在实现反向代理等复杂功能时,仍需开发者谨慎处理错误和资源管理。