我们需要从运行在Heroku上的Django admin导出包含模型数据的csv文件。因此,我们创建了一个操作,我们在其中创建了csv并将其返回给响应。这工作正常,直到我们的客户端开始导出大量数据并且我们遇到了Web worker的30秒超时。
为了避免这个问题,我们考虑将csv流式传输到客户端,而不是先在内存中构建它并将其一起发送。 Trigger是这条信息:
Cedar支持长轮询和流式响应。您的应用程序有一个30秒的初始窗口 用单个字节回复给客户端。发送完每个字节后(从>客户端收到或由您的应用程序发送),您将重置一个滚动的55秒窗口。如果没有数据>在55秒窗口期间发送,您的连接将被终止。
因此,我们实施了类似的测试方法:
import cStringIO as StringIO
import csv, time
def csv(request):
csvfile = StringIO.StringIO()
csvwriter = csv.writer(csvfile)
def read_and_flush():
csvfile.seek(0)
data = csvfile.read()
csvfile.seek(0)
csvfile.truncate()
return data
def data():
for i in xrange(100000):
csvwriter.writerow([i,"a","b","c"])
time.sleep(1)
data = read_and_flush()
yield data
response = HttpResponse(data(), mimetype="text/csv")
response["Content-Disposition"] = "attachment; filename=test.csv"
return response
下载的HTTP标头如下所示(来自FireBug):
HTTP/1.1 200 OK
Cache-Control: max-age=0
Content-Disposition: attachment; filename=jobentity-job2.csv
Content-Type: text/csv
Date: Tue, 27 Nov 2012 13:56:42 GMT
Expires: Tue, 27 Nov 2012 13:56:41 GMT
Last-Modified: Tue, 27 Nov 2012 13:56:41 GMT
Server: gunicorn/0.14.6
Vary: Cookie
Transfer-Encoding: chunked
Connection: keep-alive
“Transfer-encoding:chunked”表示Cedar实际上是按照我们猜测的方式流式传输内容。
问题是在Heroku日志中使用这些行30秒后csv的下载仍然中断:
2012-11-27T13:00:24+00:00 app[web.1]: DEBUG: exporting tasks in csv-stream for job id: 56,
2012-11-27T13:00:54+00:00 app[web.1]: 2012-11-27 13:00:54 [2] [CRITICAL] WORKER TIMEOUT (pid:5)
2012-11-27T13:00:54+00:00 heroku[router]: at=info method=POST path=/admin/jobentity/ host=myapp.herokuapp.com fwd= dyno=web.1 queue=0 wait=0ms connect=2ms service=29480ms status=200 bytes=51092
2012-11-27T13:00:54+00:00 app[web.1]: 2012-11-27 13:00:54 [2] [CRITICAL] WORKER TIMEOUT (pid:5)
2012-11-27T13:00:54+00:00 app[web.1]: 2012-11-27 13:00:54 [12] [INFO] Booting worker with pid: 12
这应该在概念上有效,对吧?我们错过了什么吗?
我们非常感谢您的帮助。 汤姆
答案 0 :(得分:6)
我找到了问题的解决方案。它不是Heroku超时,因为否则Heroku日志中会出现H12超时(感谢Heroku的Caio指出它)。
问题是Gunicorn的默认超时是30秒。在向Procfile(在Gunicorn线)添加--outoutout 600之后,问题就消失了。
Procfile现在看起来像这样:
web: gunicorn myapp.wsgi -b 0.0.0.0:$PORT --timeout 600
celeryd: python manage.py celeryd -E -B --loglevel=INFO
答案 1 :(得分:0)
这不是你的脚本问题,而是30秒web请求默认Heroku超时的问题。 你可以读到这个: https://devcenter.heroku.com/articles/request-timeout 并根据此文档 - 将您的CSV导出移至后台进程。