Reputation: 453
I am new to Asynchronous programming and a following the Bottlepy-gevent tutorial here.
I ran the program given on the page:
from gevent import monkey; monkey.patch_all()
from time import sleep
from bottle import route, run
@route('/stream')
def stream():
yield 'START'
sleep(3)
yield 'MIDDLE'
sleep(5)
yield 'END'
run(host='0.0.0.0', port=8080, server='gevent')
and it works as expected when i access the URL http://localhost:8080/stream
, printing START
followed by MIDDLE
and END
at a sleep interval of 3 and 5 seconds respectively.
As per the docs,
monkey patching enables gevent to prevent Python’s blocking APIs (and functions like time.sleep()) from blocking the current thread, and passes the CPU to the next greenlet instead.
However, when I modified the above code to print the current greenlet info, I got the same greenlet instance being used for all the three yield
statements.
import gevent
from gevent import monkey
monkey.patch_all()
from time import sleep
from bottle import route, run
@route('/stream')
def stream():
print(gevent.getcurrent())
yield 'START '
sleep(3)
print(gevent.getcurrent())
yield 'MIDDLE '
sleep(5)
print(gevent.getcurrent())
yield 'END'
run(host='0.0.0.0', port=8080, server='gevent')
Console output:
<Greenlet at 0x7f9fd733a9c8: _handle_and_close_when_done(<bound method WSGIServer.handle of <WSGIServer at , <bound method StreamServer.do_close of <WSGIServer, (<gevent._socket3.socket object, fd=7, family=2, t)>
<Greenlet at 0x7f9fd733a9c8: _handle_and_close_when_done(<bound method WSGIServer.handle of <WSGIServer at , <bound method StreamServer.do_close of <WSGIServer, (<gevent._socket3.socket object, fd=7, family=2, t)>
<Greenlet at 0x7f9fd733a9c8: _handle_and_close_when_done(<bound method WSGIServer.handle of <WSGIServer at , <bound method StreamServer.do_close of <WSGIServer, (<gevent._socket3.socket object, fd=7, family=2, t)>
Per the bottlepy docs, shouldn't a new greenlet instance be spawned each time the time.sleep() is executed?
Moreover, when I run a similar program without using gevent and monkey-patching, as below:
from time import sleep
from bottle import route, run
@route('/stream')
def stream():
yield 'START \n'
sleep(3)
yield 'MIDDLE \n'
sleep(5)
yield 'END'
run(host='0.0.0.0', port=8080)
I get the same response as before(START followed by MIDDLE and END at 3 and 5 sec), contrary to the Bottlepy docs' comment, in boldface below:
If you run this script and point your browser to http://localhost:8080/stream, you should see START, MIDDLE, and END show up one by one (rather than waiting 8 seconds to see them all at once).
Am i missing something here?
Upvotes: 0
Views: 768
Reputation: 18148
Per the bottlepy docs, shouldn't a new greenlet instance be spawned each time the time.sleep() is executed?
No. The Bottle docs are a little confusing there, I'm afraid.
A new greenlet is spawned for every incoming HTTP request. The spawned greenlet then handles the entire request, from start to finish. If that greenlet yields (either explicitly or implicitly), then another greenlet is free to do some work (presumably handling some other HTTP request).
When you call the monkeypatched time.sleep
, the greenlet that is handling your HTTP request is suspended, thereby yielding to any other active greenlets (if any). When time.sleep
returns (after the specified number of seconds) your greenlet--the very same one that was servicing your request previously--is awakened and resumes running from just after the sleep
call.
Hope that helps!
Upvotes: 0
Reputation: 8066
Note: Some browsers buffer a certain amount of data before they start rendering a page. You might need to yield more than a few bytes to see an effect in these browsers. Additionally, many browsers have a limit of one concurrent connection per URL. If this is the case, you can use a second browser or a benchmark tool (e.g. ab or httperf) to measure performance.
Upvotes: 0