potrzebujemy wyeksportować plik CSV zawierający dane z modelu z Django admin, który działa na Heroku. Dlatego stworzyliśmy akcję, w której stworzyliśmy plik CSV i zwróciliśmy go w odpowiedzi. To działało dobrze, dopóki nasz klient nie zaczął eksportować ogromnych zestawów danych, a my mamy do czynienia z 30-sekundowym czasem oczekiwania pracownika sieci.Eksport CSV w strumieniu (z administratora Django na Heroku)
Aby ominąć ten problem, pomyśleliśmy o streamowaniu CSV do klienta, zamiast budowania go najpierw w pamięci i wysyłania go w jednym kawałku. Trigger był tą informacją:
Cedr obsługuje odpowiedzi z długim odpytywaniem i przesyłaniem strumieniowym. Twoja aplikacja ma początkowe 30-sekundowe okno , aby odpowiedzieć pojedynczym bajtem z powrotem do klienta. Po wysłaniu każdego bajtu (otrzymanego od> klienta lub wysłanego przez twoją aplikację) zresetujesz trwające 55 sekund okno. Jeśli żadne dane nie zostaną> wysłane podczas 55-sekundowego okna, twoje połączenie zostanie zakończone.
dlatego Wdrożyliśmy coś, co wygląda tak, aby go przetestować:
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
Nagłówek HTTP pobrania wygląda następująco (od 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 kodowania : chunked "wskazywałby, że Cedar faktycznie strumieniuje treści, o których sądzimy.
Problem jest, że pobieranie z csv jest nadal przerwane po 30 sekund z tych linii w dzienniku 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
ten powinien pracować koncepcyjnie, prawda? Czy jest coś, co przegapiliśmy?
Naprawdę doceniamy Twoją pomoc. Tom
Ale nie powinno się okno 30 sekund czasu oczekiwania zostać przedłużony, ponieważ strumieniowo zawartość, zamiast czekać, aż CSV został stworzony w pamięci? Tak więc w tym 30-sekundowym oknie są przesyłane bajty, co pozwoli uniknąć przekroczenia limitu czasu zgodnie z poniższym: Cedar obsługuje funkcje HTTP 1.1, takie jak odpowiedzi z długim odpytywaniem i przesyłaniem strumieniowym. Aplikacja ma początkowe 30-sekundowe okno odpowiadające jednemu bajtowi z powrotem do klienta. Jednak każdy bajt transmitowany następnie resetuje ruchome okno 55 sekund. – Tom
Czy to możliwe, że Django ma wewnętrzny limit czasu podczas wysyłania odpowiedzi? – Tom
Twoje żądanie internetowe działa przez ponad 30 sekund - to fakt, a Heroku ma 30-sekundowy domyślny limit czasu dla dowolnego żądania internetowego w jego konfiguracji serwera http. Przypuszczam, że twoje próby emulacji sesji keepalive nie zakończą się sukcesem - powinieneś rozważyć przeniesienie długiego przetwarzania plików do procesu/demona w tle. – moonsly