Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Server sent events with Flask/Redis: how can more than one client view a stream?

I have multiple clients trying to connect to a server sent events stream at /stream. This works with a single client, but attempting to connect any more clients results in the new client becoming indefinitely blocked waiting for data. If I send more data, it only goes to the first client, and no others.

Here is a small snippet that illustrates my problem:

import flask
import time

app = flask.Flask(__name__)

def event_stream():
    for i in xrange(9999):
        yield "data: %d\n\n" % i
        time.sleep(1)

@app.route("/stream", methods=[ "GET" ])
def stream():
    return flask.Response(
        event_stream(),
        mimetype="text/event-stream"
    )

I then run this with gunicorn --worker-class=gevent -w 4 -t 99999 app:app. It works for a single client, but any others get blocked when issuing GET /stream.

What is the cause of the block, and how should I fix it?

I debugged a little more and got some strange results. If I do this procedure, then this happens:

  • Start client 1 (only client 1 receiving data)
  • Start client 2 (only client 1 receiving data)
  • Start client 3 (only client 1 receiving data)
  • Start client 4 (only client 1 receiving data)
  • Restart client 1 (all 4 clients suddenly start receiving data at the same time)
like image 783
Chris Down Avatar asked Jun 23 '13 09:06

Chris Down


2 Answers

It turns out that this is something to do with the Chromium web browser, where I was testing. It holds back on making the request until the first one completes, for some reason. Using curl, or an incognito browser session allowed multiple sessions to run at the same time. This means that my problem doesn't really exist in reality, it just appears that way because of the way that Chromium handles simultaneous requests to the same resource.

I'm not sure quite why Chromium behaves this way, it seems weird. Either way, this isn't a real problem, only a perceived one by my browser.

like image 115
Chris Down Avatar answered Sep 23 '22 23:09

Chris Down


I was getting similar results in Firefox (as I noted in the comments) then I switched to using WSGIServer in the main block instead of gunicorn and everything works, the timeout is gone (because WSGIServer doesn't timeout its workers but gunicorn does) so I thought this made it worth adding as an answer.

Add this:

if __name__ == '__main__':
http_server = WSGIServer(('127.0.0.1', 8001), app)
http_server.serve_forever()

Then just do

python app.py

[I would not have had a timeout after 30s if I had used Chris' command line and set timeout to 99999 but there would have been much later]

like image 40
Spaceghost Avatar answered Sep 22 '22 23:09

Spaceghost