satvik.t
satvik.t

Reputation: 453

Spawning multiple greenlets using Bottlepy-Gevent

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

Answers (2)

ron rothman
ron rothman

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

Yoav Glazner
Yoav Glazner

Reputation: 8066

2 Answers:

  • Each request from the server will spawn a new greenlet, so in the same request you'll see the same one pause and resume. try sending a few requests in the same time to see more greelets print out.
  • Your example works on my Mac with chrome the bottle docs state that you may need to yield more bytes on some browsers:

    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

Related Questions