Re: [Web-SIG] Could WSGI handle Asynchronous response?
On Jul 28, 2008, at 6:21 PM, Phillip J. Eby wrote: At 04:57 PM 7/28/2008 -0700, Donovan Preston wrote: On Jul 28, 2008, at 12:52 PM, Etienne Robillard wrote: On Mon, 18 Feb 2008 04:23:38 -0800 (PST) est <[EMAIL PROTECTED]> wrote: I am writing a small 'comet'-like app using flup, something like this: So is WSGI really synchronous? How can I handle asynchronous outputs with flup/WSGI ? WSGI says that the entire body should be written by the time the wsgi application returns. No, it doesn't. It says that all your write() calls must be done by then, which is not at all the same thing. If the application returns an iterator, that iterator can keep yielding outputs until the (figurative) cows come home. Hmm, I see what you are saying. I hadn't thought about returning an iterable instead of just using a generator. Cool. So yes it is really synchronous; as Manlio Perillo said in another message it is possible to abuse generators to allow a wsgi application to operate in the fashion you desire, but both the server and the application have to know how to do this and there is no standardization yet. This is confusing asynchronous APIs, non-blocking behavior, and streaming output. A WSGI application can avoid blocking the server by yielding empty strings until it is ready to produce more output. (This may not provide any performance benefit over sleep() however, and may in some circumstances be worse.) You're right. But continually yielding empty strings is basically busy- waiting, which would result in terrible performance, as you mention. There is no async API that's part of WSGI itself, and it's unlikely there will ever be one unless there ends up being an async API for Python as well. I know this has been discussed before on the list and I wasn't really paying attention enough to know what was proposed, but it seems to me that just having a well-defined way for the application to tell the server when to resume the iterable is possible. Manlio has come up with an API for this in his nginx mod_wsgi. For example, something like the interface to select could be used: def foo(env, start_response): my_sock = socket.socket() my_sock.setblocking(0) my_sock.connect((...)) r, w, e = yield [[my_sock.fileno()], [], [my_sock.fileno()]] if e: ... bytes = my_sock.recv(4096) This requires 2.5's extended generators, but the file descriptor readiness lists could be put in the environ before resuming the iterator for people who don't want to or can't move to 2.5. This is just an example, I think Manlio's api (which is more like poll if I remember correctly) is better. Really I don't actually care, since eventlet and greenlet let me mash together wsgi applications written with blocking i/o style with an http server that does non-blocking i/o. (By the way, using a generator to produce streaming output is not abuse: it is the *intended* use of iterables in WSGI!) Nice. Donovan ___ Web-SIG mailing list Web-SIG@python.org Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com
Re: [Web-SIG] Could WSGI handle Asynchronous response?
At 04:57 PM 7/28/2008 -0700, Donovan Preston wrote: On Jul 28, 2008, at 12:52 PM, Etienne Robillard wrote: On Mon, 18 Feb 2008 04:23:38 -0800 (PST) est <[EMAIL PROTECTED]> wrote: I am writing a small 'comet'-like app using flup, something like this: So is WSGI really synchronous? How can I handle asynchronous outputs with flup/WSGI ? WSGI says that the entire body should be written by the time the wsgi application returns. No, it doesn't. It says that all your write() calls must be done by then, which is not at all the same thing. If the application returns an iterator, that iterator can keep yielding outputs until the (figurative) cows come home. So yes it is really synchronous; as Manlio Perillo said in another message it is possible to abuse generators to allow a wsgi application to operate in the fashion you desire, but both the server and the application have to know how to do this and there is no standardization yet. This is confusing asynchronous APIs, non-blocking behavior, and streaming output. A WSGI application can avoid blocking the server by yielding empty strings until it is ready to produce more output. (This may not provide any performance benefit over sleep() however, and may in some circumstances be worse.) There is no async API that's part of WSGI itself, and it's unlikely there will ever be one unless there ends up being an async API for Python as well. (By the way, using a generator to produce streaming output is not abuse: it is the *intended* use of iterables in WSGI!) ___ Web-SIG mailing list Web-SIG@python.org Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com
Re: [Web-SIG] Could WSGI handle Asynchronous response?
At 04:23 AM 2/18/2008 -0800, est wrote: I am writing a small 'comet'-like app using flup, something like this: def myapp(environ, start_response): start_response('200 OK', [('Content-Type', 'text/plain')]) return ['Flup works!\n']<-Could this be part of response output? Could I time.sleep() for a while then write other outputs? if __name__ == '__main__': from flup.server.fcgi import WSGIServer WSGIServer(myapp, multiplexed=True, bindAddress=('0.0.0.0', )).run() So is WSGI really synchronous? How can I handle asynchronous outputs with flup/WSGI ? You are confusing "asynchronous" with "streaming". WSGI is synchronous, but allows streaming and "server push". Instead of returning a sequence, code your application as an iterator that yields output chunks. It is "synchronous" in the sense that if you sleep or do processing in between yielded output chunks, you will prevent the server from freeing any resources associated with your application, or from doing any other work in the current thread. A properly-designed WSGI server should continue to function, as long as all available resources aren't consumed... which in the case of "push" apps could easily make your box fall over, regardless of whether WSGI is involved. :) ___ Web-SIG mailing list Web-SIG@python.org Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com
Re: [Web-SIG] Could WSGI handle Asynchronous response?
On Jul 28, 2008, at 12:52 PM, Etienne Robillard wrote: On Mon, 18 Feb 2008 04:23:38 -0800 (PST) est <[EMAIL PROTECTED]> wrote: I am writing a small 'comet'-like app using flup, something like this: So is WSGI really synchronous? How can I handle asynchronous outputs with flup/WSGI ? WSGI says that the entire body should be written by the time the wsgi application returns. So yes it is really synchronous; as Manlio Perillo said in another message it is possible to abuse generators to allow a wsgi application to operate in the fashion you desire, but both the server and the application have to know how to do this and there is no standardization yet. maybe start by looking here: http://twistedmatrix.com/trac/browser/trunk/twisted/web2/wsgi.py web2.wsgi's server doesn't really get around the problem. While it does non-blocking i/o for the http request and response, it actually calls the wsgi application in a threadpool, because there's no way for the wsgi application to return before having generated all of the response, and even if there were people's wsgi applications don't work this way. You might want to check out orbited (http://www.orbited.org/), which doesn't have anything to do with wsgi, but is a Python comet server implemented entirely with non-blocking i/o (using libevent). However, if you are willing to spend some time getting a custom comet server up and running, you could take a look at eventlet (http://pypi.python.org/pypi/eventlet/ ) and spawning (http://pypi.python.org/pypi/Spawning/). I've been working on eventlet for a couple of years precisely to make implementing scalable and easy to maintain comet applications possible. Here's a simple Comet server that uses spawning and eventlet. This will give you a comet server that scales to tons of simultaneous connections, because eventlet mashes together greenlet (coroutines, or light-weight cooperative threads) with non-blocking i/o (select, poll, libevent, or libev). This is how Spawning can be used to get around the wsgi restriction that the entire body should be written by the time the wsgi application returns; since spawning uses greenlets instead of posix threads for each wsgi request when --threads=0 is passed, many simultaneous wsgi applications can be running waiting for Comet events with very little memory and CPU overhead. Save it in a file called spawningcomet.py and run it with: spawn spawningcomet.wsgi_application --threads=0 Then, visit http://localhost:8080 in your browser and run this in another terminal: python spawningcomet.py hello world ## spawningcomet.py import struct import sys import uuid from eventlet import api from eventlet import coros SEND_EVENT_INTERFACE = '' SEND_EVENT_PORT = 4200 HTML_TEMPLATE = """ Dynamic content will appear below """ class Comet(object): def __init__(self): api.spawn( api.tcp_server, api.tcp_listener((SEND_EVENT_INTERFACE, SEND_EVENT_PORT)), self.read_events_forever) self.current_event = {'event': coros.event(), 'next': None} self.first_event_id = str(uuid.uuid1()) self.events = {self.first_event_id: self.current_event} def read_events_forever(self, (sock, addr)): reader = sock.makefile('r') try: while True: ## Read the next event value out of the socket valuelen = reader.read(4) if not valuelen: break valuelen, = struct.unpack('!L', valuelen) value = reader.read(valuelen) ## Make a new event and link the current event to it old_event = self.current_event old_event['next'] = str(uuid.uuid1()) self.current_event = { 'event': coros.event(), 'next': None} self.events[old_event['next']] = self.current_event ## Send the event value to any waiting http requests old_event['event'].send(value) finally: reader.close() sock.close() def __call__(self, env, start_response): if env['REQUEST_METHOD'] != 'GET': start_response('405 Meth
Re: [Web-SIG] Could WSGI handle Asynchronous response?
est ha scritto: I am writing a small 'comet'-like app using flup, something like this: def myapp(environ, start_response): start_response('200 OK', [('Content-Type', 'text/plain')]) return ['Flup works!\n']<-Could this be part of response output? What do you mean by "part of response output"? Could I time.sleep() for a while then write other outputs? Not with flup. if __name__ == '__main__': from flup.server.fcgi import WSGIServer WSGIServer(myapp, multiplexed=True, bindAddress=('0.0.0.0', )).run() So is WSGI really synchronous? Not really. Since you can return a generator, it's possible to support asynchronous programming, but the WSGI gateway must support it, as an example with Nginx mod_wsgi and some other implementations (search in the mailing list archive). But this support has not been standardized. How can I handle asynchronous outputs with flup/WSGI ? Regards Manlio Perillo ___ Web-SIG mailing list Web-SIG@python.org Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com
Re: [Web-SIG] Could WSGI handle Asynchronous response?
On Mon, 18 Feb 2008 04:23:38 -0800 (PST) est <[EMAIL PROTECTED]> wrote: > I am writing a small 'comet'-like app using flup, something like > this: > > def myapp(environ, start_response): > start_response('200 OK', [('Content-Type', 'text/plain')]) > return ['Flup works!\n']<-Could this be part > of response output? Could I time.sleep() for a while then write other > outputs? > > > if __name__ == '__main__': > from flup.server.fcgi import WSGIServer > WSGIServer(myapp, multiplexed=True, bindAddress=('0.0.0.0', > )).run() > > > So is WSGI really synchronous? How can I handle asynchronous outputs > with flup/WSGI ? maybe start by looking here: http://twistedmatrix.com/trac/browser/trunk/twisted/web2/wsgi.py Regards, Etienne ___ Web-SIG mailing list Web-SIG@python.org Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com
[Web-SIG] Could WSGI handle Asynchronous response?
I am writing a small 'comet'-like app using flup, something like this: def myapp(environ, start_response): start_response('200 OK', [('Content-Type', 'text/plain')]) return ['Flup works!\n']<-Could this be part of response output? Could I time.sleep() for a while then write other outputs? if __name__ == '__main__': from flup.server.fcgi import WSGIServer WSGIServer(myapp, multiplexed=True, bindAddress=('0.0.0.0', )).run() So is WSGI really synchronous? How can I handle asynchronous outputs with flup/WSGI ? ___ Web-SIG mailing list Web-SIG@python.org Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com