在Heroku上使用Flask:大POST数据时request.form极慢?
我在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 个回答
我想我明白发生了什么事。简单来说,服务器并没有慢,反而是我被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的指标变得不那么有用,但实际上并不影响最终用户的体验。