Gin 上传大文件失败:net::ERR_CONNECTION_RESET

标签: 问题解决 Gin Go 发布于:2022-11-21 15:27:34 编辑于:2022-11-21 17:16:47 浏览量:30

问题描述

我在开发 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 中引入了更多的中间件,全部禁用掉试一下。很遗憾,依旧有问题。

不过我在测试的时候,发现了一些有意思的事情:

  1. 1800 KB 的文件,每次都能上传成功。
  2. 2354 KB 的文件,每次都是卡在 95.19%,然后报错。
  3. 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 的问题,标准库的问题,还是我写的用户代码的问题都没能确定,很乱。

整理思绪

目前感觉很奇怪的地方:

  1. 线上环境没有此问题,本地测试有问题,使用和线上环境相同的可执行文件一样有问题,但是本地执行时用的是 WSL2,同时线上环境用 Nginx 做了 HTTP2 的反向代理。
  2. 小文件没有问题,大于 2MB 左右的文件有问题。
    • 上面的 b.buf,我打了断点看了下大小是 4096,既然是 buffer,单位应该是 Byte,和上面的 2 MB 又有什么联系呢?
  3. 浏览器每次都是 Network Error(即 ECONNRESET),Postman 现在每次都是 multipart: NextPart: bufio: buffer full

未经允许,禁止转载,本文源站链接:https://iamazing.cn/