Flask+Gunicorn+Nginx大文件上传超时连接重置问题求助
大文件上传进度冻结及连接重置问题解决
问题背景
搭建基于Flask、Gunicorn、Nginx的个人服务器,实现带进度显示的文件上传功能,但上传大文件(耗时超30秒)时,进度会在30秒时冻结,约1分钟后恢复,5秒后再次冻结,最终3.5分钟左右出现net::ERR_CONNECTION_RESET错误。
已尝试无效方案
- 使用Gunicorn的
geventworker,并在启用/禁用该worker时测试 - Gunicorn启动参数添加
--log-level debug --keep-alive 30 --timeout 300 --graceful-timeout 300,无变化且调试日志无上传相关信息 - Nginx的http段添加
proxy_connect_timeout 300s;、proxy_read_timeout 300s;和client_max_body_size 100G;
有效解决步骤
1. 完善Nginx代理配置
Nginx的http段配置可能未覆盖到具体代理location,需在代理到Gunicorn的location块中补充以下配置,确保上传过程的超时控制和实时进度传递:
location / { # 保留原有代理配置(如proxy_pass http://127.0.0.1:8000;) client_body_timeout 300s; # 客户端请求体传输超时 proxy_send_timeout 300s; # Nginx向Gunicorn发送请求超时 proxy_read_timeout 300s; # Nginx等待Gunicorn响应超时 proxy_connect_timeout 300s; # Nginx与Gunicorn建立连接超时 proxy_buffering off; # 禁用代理缓冲,让进度事件实时传递到前端 chunked_transfer_encoding on; # 启用分块传输,适配大文件上传场景 }
2. 调整Gunicorn与Flask配置
- 若使用
geventworker,确保已安装gevent,启动参数调整为:
(gunicorn -w 4 -k gevent --timeout 300 --graceful-timeout 300 --keep-alive 300 --log-level debug app:app-w 4根据服务器CPU核心数调整,--keep-alive设为300避免连接过早断开) - 在Flask应用中添加大文件限制配置,避免Flask层面截断请求:
app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024 * 1024 # 100G,与Nginx配置保持一致
3. 优化Flask上传处理逻辑
默认的file.save()会一次性将大文件读取到内存,导致Gunicorn worker长时间阻塞,引发超时。改为分块读取保存,释放worker资源:
# 替换原views.py中的file.save(...)代码 file_path = os.path.join(BASE_DIR + path, secured_filename) chunk_size = 1024 * 1024 # 按1MB分块读取 with open(file_path, 'wb') as f: while chunk := file.stream.read(chunk_size): f.write(chunk)
4. 检查服务器系统级超时设置
- 检查Linux系统TCP连接超时参数,若默认值过小,可临时调整:
echo 300 > /proc/sys/net/ipv4/tcp_keepalive_time echo 60 > /proc/sys/net/ipv4/tcp_keepalive_intvl echo 5 > /proc/sys/net/ipv4/tcp_keepalive_probes - 检查云服务器安全组或防火墙是否有连接超时限制,确保TCP连接超时设置≥300秒。
5. 前端错误监听优化
添加错误和超时监听,便于后续排查问题:
function uploadFile(path) { let formdata = new FormData(); let req = new XMLHttpRequest(); let filesize = document.getElementById("fileInput").files[0].size; formdata.append("fileinput", document.getElementById("fileInput").files[0]); req.upload.addEventListener('progress', (uploadEvent) => { let progressElem = document.getElementById('progressIndicator'); if (uploadEvent.loaded < filesize) { progressElem.innerHTML = (100 * uploadEvent.loaded / filesize).toFixed(2) + '%'; } else { progressElem.style.color = "green"; progressElem.innerHTML = "✅ 文件上传完成"; } }); // 上传错误监听 req.addEventListener('error', (e) => { console.error('上传错误详情:', e); const progressElem = document.getElementById('progressIndicator'); progressElem.style.color = "red"; progressElem.innerHTML = "❌ 上传失败: 连接重置"; }); // 上传超时监听 req.addEventListener('timeout', () => { const progressElem = document.getElementById('progressIndicator'); progressElem.style.color = "red"; progressElem.innerHTML = "❌ 上传超时"; }); req.open("POST", path); req.timeout = 300000; // 设置请求超时为5分钟 req.send(formdata); }
内容的提问来源于stack exchange,提问作者Biskweet
相关产品推荐
相关产品推荐

