Gin 上传大文件失败:net::ERR_CONNECTION_RESET
问题描述
我在开发 Gin Template 的时候,需要做一个文件上传的功能,遇到了这个问题。
小文件上传没问题,大文件上传会报错。
react-scripts 的报错:
Proxy error: Could not proxy request /api/file from localhost:3001 to http://localhost:3000/.
See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (ECONNRESET).
Postman 的报错:Could not get response Error: write ECONNRESET
最为诡异的事情是,服务端的日志状态码显示为 200,此外也没有别的日志:
[GIN] 2022/11/21 - 15:32:38 | 200 | 224.999ms | ::1 | POST "/api/file/"
[GIN] 2022/11/21 - 15:32:52 | 200 | 263.6781ms | ::1 | POST "/api/file/"
而且有时候根本没有任何相关日志!
这就很离谱了,因为我之前是做过文件上传的(Go File),代码也是照搬过来核心部分没有做改动。
我也试了下把代码完全照搬过来,只修改掉必须修改的部分,结果还是一样的问题。
问题排查
首先,Go File 是 work 的,我使用本地的 Go 版本重新编译 Go File,大文件上传依旧正常。
接下来,排查 Gin 的版本,Go File 的 Gin 版本较低,为 v1.7.4
,而 Gin Template 的版本为 v1.8.1
(当前最新版本)。
尝试将 Go File 的版本升级至最新,测试之后没有问题。
除此之外,Gin Template 中引入了更多的中间件,全部禁用掉试一下。很遗憾,依旧有问题。
不过我在测试的时候,发现了一些有意思的事情:
- 1800 KB 的文件,每次都能上传成功。
- 2354 KB 的文件,每次都是卡在 95.19%,然后报错。
- 19272 KB 的文件,每次都是卡在 11.62%,然后报错。
算了一下:
In [1]: 2354 * 0.9519
Out[1]: 2240.7726
In [2]: 19272 * 0.1162
Out[2]: 2239.4064
这两个值非常接近,与之最为接近的 2 的整数次幂是 2048 KB,也就是 2 MB。
这个时候我才想到,线上环境好像没有这个问题哦,只是我开发环境有问题。
线上环境是 GitHub Actions 打包的可执行文件,我在本地测试了 Windows 和 Linux 的版本,结果一样有文件上传失败的问题。
这就很离谱了,一样的程序欸。
拿这 2 MB 的信息做进一步的搜索,很遗憾,关于 Golang 的并不多。
在进一步的测试过程中,我发现使用 Postman 去测试有时候能得到 JSON response:
{
"message": "multipart: NextPart: bufio: buffer full",
"success": false
}
这个返回来自这里:
form, err := c.MultipartForm()
if err != nil {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": err.Error(),
})
return
}
层层追进去,发现是这里报的错:
// mime/multipart/multipart.go
line, err := r.bufReader.ReadSlice('\n')
// bufio/bufio.go
// Buffer full?
if b.Buffered() >= len(b.buf) {
b.r = b.w
line = b.buf
err = ErrBufferFull
break
}
这里感觉就很离谱了,不至于标准库有问题吧。
而且,为什么我现在用 Postman 测试,每次都能得到 JSON 返回值,而不是之前的 ECONNRESET
?
而浏览器测试每次都是 Network Error
?
很烦,现在到底是浏览器的问题,服务端的问题,Gin 的问题,标准库的问题,还是我写的用户代码的问题都没能确定,很乱。
整理思绪
目前感觉很奇怪的地方:
- 线上环境没有此问题,本地测试有问题,使用和线上环境相同的可执行文件一样有问题,但是本地执行时用的是 WSL2,同时线上环境用 Nginx 做了 HTTP2 的反向代理。
- 小文件没有问题,大于 2MB 左右的文件有问题。
- 上面的
b.buf
,我打了断点看了下大小是 4096,既然是 buffer,单位应该是 Byte,和上面的 2 MB 又有什么联系呢?
- 上面的
- 浏览器每次都是
Network Error
(即ECONNRESET
),Postman 现在每次都是multipart: NextPart: bufio: buffer full
。