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?