6

I have an algorithm which waits for almost 5 seconds to generate response and I want to send an ack (http 200) to user as soon as he sends request to tell him that his request has been received and wait for 5 seconds.

Generator function:

def chunked_res():
    yield "Chunk 1"

    stop = time.time() + 5    # wait for 5 seconds
    while time.time() < stop:
        pass

    yield "Chunk 2"

And in some view:

response = HttpResponse ( chunked_res() )
response['Connection'] = 'close'
response['Transfer-Encoding'] = 'chunked'
response['status'] = 200
return response

Response in browser:

"Transfer-Encoding: chunked\nstatus: 200\nConnection: close\nContent-Type: text/html; charset=utf-8\n\nChunk 1Chunk 2"

Problem: I am getting required response(Chunk 1, Chunk 2) but after 5 seconds. I want to send "Chunk 1" first and then "Chunk 2" after 5 seconds(update respose). Are there any particular settings/changes to implement this?

Update:

Django = 1.4 python = 2.7

Zubair Afzal
  • 2,016
  • 20
  • 29

4 Answers4

3

Actually the solution was to make first chunk size of at least 1024 character for browser to show incrementally.

How to stream an HttpResponse with Django

def chunked_res():
    yield "Chunk 1"
    yield " " * 1024  # Encourage browser to render incrementally (either 1024 or 1024-7{length of "chunk 1"} = 1017)

    time.sleep(5)  # wait for 5 seconds

    yield "Chunk 2"


def myview(request):
    g = chunked_res()
    return HttpResponse(g)

If you are using nginx then you have to set proxy_buffering=off, for server to flush response as 1024 data chunk ready. http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffer_size

Works with: HTTP/1.0 and HTTP/1.1

Community
  • 1
  • 1
Zubair Afzal
  • 2,016
  • 20
  • 29
2

They just added StreamingHttpResponse in django 1.5, and by passing an iterator in earlier versions (<= 1.4) , you can stream ( or "chunk"), data.

def chunked_res():
    yield "Chunk 1"

    time.sleep(5)  # wait for 5 seconds

    yield "Chunk 2"


def myview(request):
    g = chunked_res()
    return HttpResponse(g)
Ellis Percival
  • 4,632
  • 2
  • 24
  • 34
monkut
  • 42,176
  • 24
  • 124
  • 155
  • Thanks for quick response but I am using django 1.4. I have updated my question (django=1.4 and python 2.7). – Zubair Afzal Mar 18 '13 at 07:05
  • 1
    Yes, like I mentioned, you can pass an iterator to django's standard HttpResponse in 1.4. – monkut Mar 18 '13 at 07:11
  • Tried your solution by directly passing function output in HttpResponse but the issue is same. I get full response after 5 seconds at once. – Zubair Afzal Mar 18 '13 at 07:20
  • Not the function output, the iterator should be passed. – monkut Mar 18 '13 at 07:32
  • Check out this question/answer. It sounds like middleware may be getting in the way. http://stackoverflow.com/questions/2922874/how-to-stream-an-httpresponse-with-django – monkut Mar 18 '13 at 22:09
2

You really want to avoid doing this...

stop = time.time() + 5    # wait for 5 seconds
while time.time() < stop:
    pass

It will cause the CPU usage of your process to spike to 100% for 5 seconds, which is a huge amount for a web application. In a shared hosting environment you risk getting a nasty email from your hosting provider, and if you've got your own server, those cycles you're eating up in the loop could be usefully deployed in handling other requests. Do this instead:

time.sleep(5)

That will put the thread (or process) to sleep for 5 seconds, and the kernel is then free to schedule some other task, or sleep the CPU (saving power and cooling costs) if there's nothing else to do.

1

Just clarifying the other answers: for Django 1.5+, you must use StreamingHttpResponse. Regular HttpResponse will collect the full response and return it only when finished.

from django.http import StreamingHttpResponse

def chunked_res():
    yield "Chunk 1"
    yield " " * 1024  # Encourage server to begin transferring and browser to begin rendering

    time.sleep(5)     # wait for 5 seconds

    yield "Chunk 2"

def myview(request):
    return StreamingHttpResponse(chunked_res())
krubo
  • 5,969
  • 4
  • 37
  • 46