3

I'm working on using gevent and tornado inside the same application so that libraries that doesn't support tornado's ioloop can be subdued to use gevent to act asynchronously. I thought I'd need to run two real systems threads, one dedicated to Tornado's ioloop and another dedicated to gevent's loop. However, trying to call any gevent function inside a system thread returns not implemented Error, gevent cannot be used inside threads. Therefore, I tried monkey patching threading as well, as the following snippet shows

from gevent import monkey; monkey.patch_all()
from random import choice
import gevent
import requests
import tornado.ioloop
import tornado.web
import threading
import Queue

q = Queue.Queue()

i = 0
def synchronous_get_url(url, callback=None):
    global i
    i += 1
    d = i

    print('bar %d getting %s' % (d, url))
    requests.get(url)
    print('bar %d finished getting %s' % (d, url))
    if callback:
        callback()

class GreenEventLoop(threading.Thread):
    daemon = True
    def run(self):
        while True:
            url, callback = q.get()
            gevent.spawn(synchronous_get_url, url, callback)

continuing...

class MainHandler(tornado.web.RequestHandler):

    @tornado.web.asynchronous
    def get(self):
        print 'Received get request'
        urls = [
            'http://google.com',
            'http://apple.com',
            'http://microsoft.com',
            'http://github.com',
            'http://sourceforge.com',
        ]

        q.put((choice(urls), self._on_fetch), block=False)
        self.write("submitted url to queue")

    def _on_fetch(self):
        print 'Finishing in the handler\n'
        try:
            self.finish()
        except:
            pass

# Start GEvent Loop
green_loop = GreenEventLoop()
green_loop.start()

# Start Tornado Loop
application = tornado.web.Application([
    (r"/", MainHandler),
    ], debug=True)
application.listen(7001)
tornado.ioloop.IOLoop.instance().start()

In a separate process, on the command line, I run the following.

from gevent import monkey; monkey.patch_all()
import gevent
import requests
count = 0
def get_stuff(i):
    global count
    res = requests.get('http://localhost:7000/')
    count += 1
    print count, res, i

lets = [gevent.spawn(get_stuff, i) for i in range(15)]
gevent.joinall(lets)

This allows retrieves 15 urls simultaneously, and return the response as they are received. What I don't quite understand is why the above code works at all. If threading is patched by gevent and turned into green threads, that means there's only ever a single thread running at a time, which means that while gevent is off fetching new responses, tornado's ioloop would block and not handle new requests until the old one has returned. Can someone explain how gevent would interact with Tornado's ioloop?

Tony
  • 36,591
  • 10
  • 48
  • 83

2 Answers2

1

I suggest you look at motor lib it's async wrapper around pymongo driver. It's uses greenlets to adopt synchronous pymongo code for tornado callbacks style. So I think it should be good place to find some ideas.

Max Kamenkov
  • 2,478
  • 3
  • 22
  • 19
0

Basic idea is to use gevent to monkey patch the system threads, then run tornado under python 'threads' which are really gevent greenlets.

How to use gevent and tornado together

Nathaniel Tucker
  • 577
  • 1
  • 5
  • 17