Re: [Web-SIG] Could WSGI handle Asynchronous response?

2008-07-28 Thread Donovan Preston


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?

2008-07-28 Thread Phillip J. Eby

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?

2008-07-28 Thread Phillip J. Eby

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?

2008-07-28 Thread Donovan Preston


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?

2008-07-28 Thread Manlio Perillo

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?

2008-07-28 Thread Etienne Robillard
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?

2008-07-28 Thread est
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