11

我们需要从运行在 Heroku 上的 Django admin 导出一个包含模型数据的 csv 文件。因此,我们创建了一个操作,我们在其中创建了 csv 并在响应中返回它。这一直很好,直到我们的客户端开始导出大量数据并且我们遇到了 Web Worker 的 30 秒超时。

为了规避这个问题,我们考虑将 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 标头如下所示(来自 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 实际上是按我们猜测的分块传​​输内容。

问题是 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

这应该在概念上有效,对吧?有什么我们错过的吗?

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

4

2 回答 2

10

我找到了问题的解决方案。这不是 Heroku 超时,因为否则 Heroku 日志中会出现 H12 超时(感谢 Heroku 的 Caio 指出这一点)。

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

Procfile 现在看起来像这样:

web: gunicorn myapp.wsgi -b 0.0.0.0:$PORT --timeout 600
celeryd: python manage.py celeryd -E -B --loglevel=INFO
于 2012-11-30T08:39:21.573 回答
1

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

于 2012-11-27T18:10:20.240 回答