2012-11-27 41 views
8

我们需要从Heroku上运行的Django admin导出包含模型数据的csv文件。因此,我们创建了一个操作,在该操作中创建了csv并将其返回给响应。这工作得很好,直到我们的客户端开始导出大量数据,并且我们遇到了Web工作者的30秒超时。Stream导出的CSV导出(来自Heroku上的Django管理员)

为了避免这个问题,我们考虑了将csv流式传输到客户端,而不是先在内存中构建它并将其发送到一个整体。触发器是这条信息:

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标头看起来像这样(从萤火虫):

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 

“传输编码

:chunked“将表明Cedar实际上是我们猜测的内容流式传输内容。

问题是CSV的下载30秒后仍然中断与Heroku的日志中的这些行:

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 

这应该工作的概念,对不对?有什么我们错过了吗?

我们非常感谢您的帮助。 汤姆

回答

6

我找到了解决问题的办法。这不是Heroku超时,因为否则在Heroku日志中会有H12超时(感谢Heroku的Caio指出)。

问题是Gunicorn的默认超时时间是30秒。在向Procfile添加 - 超时600(在Gunicorn的行)后,问题消失了。

的Procfile现在看起来是这样的:

web: gunicorn myapp.wsgi -b 0.0.0.0:$PORT --timeout 600 
celeryd: python manage.py celeryd -E -B --loglevel=INFO 
0

这不是你的脚本的问题,但30秒的Web请求默认Heroku超时的问题。 您可以阅读: https://devcenter.heroku.com/articles/request-timeout 并根据此文档 - 将CSV导出移动到后台进程。

+0

但应该不是因为我们流的内容,而不是等到CSV已在内存中创建了30秒的超时窗口延长?所以在这个30秒的窗口中有字节传输,这应该避免根据这个超时: Cedar支持HTTP 1.1功能,如长轮询和流响应。一个应用程序有一个30秒的初始窗口,用一个字节回应给客户端。然而,之后发送的每个字节重置一个滚动55秒的窗口。 – Tom

+0

发送响应时,Django是否有内部超时? – Tom

+0

您的网络请求工作时间超过30秒 - 这是一个事实,Heroku的HTTP服务器配置中的任何Web请求都有30秒的默认超时时间。我想,你试图模拟keepalive会话将不会成功 - 你最好考虑将你的长文件处理移动到后台进程/守护进程。 – moonsly