在Heroku上使用Flask:大POST数据时request.form极慢?

5 投票
1 回答
1472 浏览
提问于 2025-04-17 15:34

我在Heroku上用gunicorn和eventlet工作者运行一个Flask应用。有一个特定的路由经常接收POST数据(x-www-form-urlencoded),这些数据字段比较大,最多大约有500KB。

在本地运行时一切正常,但在Heroku上,访问这个路由的请求完成时间从5秒到30秒不等,而且几乎100%的时间都花在第一次访问request.form上:

t = time.time()
action = str(request.form['action'])
dt = time.time() - t  # Often 10 seconds or more!

Newrelic的慢请求追踪也证实了这一点。在数据库操作上花费的时间只有几毫秒,而在Python代码中却花了很长时间,显然是在等待某些输入输出操作,因为报告的CPU时间通常少于一毫秒。

我完全无法在本地环境中重现这个问题,尽管我在生产环境中使用的是相同的gunicorn/eventlet设置。即使是内置的调试WSGI服务器在处理这些请求时也非常快。

有没有人知道可能出了什么问题?是Flask的问题,还是我需要联系Heroku支持呢?

1 个回答

3

我想我明白发生了什么事。简单来说,服务器并没有慢,反而是我被Newrelic报告的响应时间给误导了!

我按照@AllanAnderson的建议,在dotCloud的沙盒环境中运行了相同的代码。我首先创建了一个简化的测试案例:一个简单的HTML表单,里面有几个隐藏字段,预先加载了大约900KB的数据,还有一个视图函数,除了从request.form字典中读取数据外,什么也不做,只是用time.time()来测量每次访问的时间。

在Heroku上,结果是这样的:

5.87100 seconds: read field "p1": 786432 bytes
0.00019 seconds: read field "p2": 131072 bytes
0.00003 seconds: read field "p3": 12288 bytes
0.00001 seconds: read field "p4": 1024 bytes

而在dotCloud上:

0.00096 seconds: read field "p1": 786432 bytes
0.00019 seconds: read field "p2": 131072 bytes
0.00003 seconds: read field "p3": 12288 bytes
0.00001 seconds: read field "p4": 1024 bytes

不过,在我的浏览器中,这两个测试似乎花费的时间是一样的……你们可能已经猜到这个“问题”的真正答案了。:-)

结果发现,Heroku上的gunicorn在收到请求头后就开始执行视图函数,而第一次访问request.form时会被阻塞,直到整个请求都接收完毕。所以Newrelic看到的那些慢响应时间,其实只是因为在一个糟糕的网络连接上上传POST数据造成的。而dotCloud的设置显然是等到整个请求都接收完了才开始处理。

这让Newrelic的指标变得不那么有用,但实际上并不影响最终用户的体验。

撰写回答