Server-sent events with Python, Twisted and Flask: is this the right approach for sleeping?

I started watching events sent by the server and became interested in testing them using my preferred tools: Python, Flask and Twisted. I ask you to sleep the way I do, itโ€™s fine compared to gentent greenlet.sleep, itโ€™s my very simple code and is โ€œportedโ€ to Twisted (from gevent):

#!/usr/bin/env python import random from twisted.web.server import Site from twisted.web.wsgi import WSGIResource from twisted.internet import reactor import time from flask import Flask, request, Response app = Flask(__name__) def event_stream(): count = 0 while True: count += 1 yield 'data: %c (%d)\n\n' % (random.choice('abcde'), count) time.sleep(1) @app.route('/my_event_source') def sse_request(): return Response( event_stream(), mimetype='text/event-stream') @app.route('/') def page(): return ''' <!DOCTYPE html> <html> <head> <script type="text/javascript" src="//code.jquery.com/jquery-1.8.0.min.js"></script> <script type="text/javascript"> $(document).ready( function() { sse = new EventSource('/my_event_source'); sse.onmessage = function(message) { console.log('A message has arrived!'); $('#output').append('<li>'+message.data+'</li>'); } }) </script> </head> <body> <h2>Demo</h2> <ul id="output"></ul> </body> </html> ''' if __name__ == '__main__': resource = WSGIResource(reactor, reactor.getThreadPool(), app) site = Site(resource) reactor.listenTCP(8001, site) reactor.run() 

Although the time.sleep function is a blocking function that will not block the Twisted reactor, and this should be confirmed by the fact that several different browsers can access this page and correctly accept this event: using different browsers is necessary in case it does Chromium, several different requests with the same URI will be queued, and since this is a response to a streaming call, this browser queue will be occupied until the socket or request is closed. What are your stiffnesses? Any better way? There are not many code examples with Twisted and Flask around.

+6
source share
1 answer

Your example uses twisted only as a wsgi container . Like any other thread-based wsgi container, you can use time.sleep(1) .

This can be useful if you allow twisting to handle /my_event_source . Here is an example from Using events dispatched by a server implemented in Python using twisted:

 def cycle(echo): # Every second, sent a "ping" event. timestr = datetime.utcnow().isoformat()+"Z" echo("event: ping\n") echo('data: ' + json.dumps(dict(time=timestr))) echo("\n\n") # Send a simple message at random intervals. if random.random() < 0.1: echo("data: This is a message at time {}\n\n".format(timestr)) class SSEResource(resource.Resource): def render_GET(self, request): request.setHeader("Content-Type", "text/event-stream") lc = task.LoopingCall(cycle, request.write) lc.start(1) # repeat every second request.notifyFinish().addBoth(lambda _: lc.stop()) return server.NOT_DONE_YET 

where the client static/index.html is from the same source :

 <!doctype html> <title>Using server-sent events</title> <ol id="eventlist">nothing sent yet.</ol> <script> if (!!window.EventSource) { var eventList = document.getElementById("eventlist"); var source = new EventSource('/my_event_source'); source.onmessage = function(e) { var newElement = document.createElement("li"); newElement.innerHTML = "message: " + e.data; eventList.appendChild(newElement); } source.addEventListener("ping", function(e) { var newElement = document.createElement("li"); var obj = JSON.parse(e.data); newElement.innerHTML = "ping at " + obj.time; eventList.appendChild(newElement); }, false); source.onerror = function(e) { alert("EventSource failed."); source.close(); }; } </script> 

You can combine it with your wsgi application:

 app = Flask(__name__) @app.route('/') def index(): return redirect(url_for('static', filename='index.html')) if __name__ == "__main__": root = resource.Resource() root.putChild('', wsgi.WSGIResource(reactor, reactor.getThreadPool(), app)) root.putChild('static', static.File("./static")) root.putChild('my_event_source', SSEResource()) reactor.listenTCP(8001, server.Site(root)) reactor.run() 

WSGIResource expects all URLs to be processed, so the routing code needs to be rewritten to support multiple jar handlers.

+4
source

Source: https://habr.com/ru/post/959333/


All Articles