📜 ⬆️ ⬇️

Inline-callback in tornado server for asyncmongo

A couple of weeks ago, the tornado developers added a native inline callback module (similar to inlineCallbacks in Twisted, Seq in Node.js, Fibers in Ruby).
Below are examples of use and examples involving asyncmongo (asynchronous driver for mongoDB)

For comparison, I will give a classic example with kalbekami
class AsyncHandler(RequestHandler): @asynchronous def get(self): http_client = AsyncHTTPClient() http_client.fetch("http://example.com", callback=self.on_fetch) def on_fetch(self, response): do_something_with_response(response) self.render("template.html") 

And so, with the new module "tornado.gen"
  class GenAsyncHandler(RequestHandler): @asynchronous @gen.engine def get(self): http_client = AsyncHTTPClient() response = yield gen.Task(http_client.fetch, "http://example.com") do_something_with_response(response) self.render("template.html") 

Calls are made through generators, like in Twisted.

You can also call an array of methods, control returns when all the specified methods are processed.
  def get(self): http_client = AsyncHTTPClient() response1, response2 = yield [gen.Task(http_client.fetch, url1), gen.Task(http_client.fetch, url2)] 


They also added a convenient mechanism (the Callback and Wait methods), to wait for all the necessary methods to be run at different times.
  class GenAsyncHandler2(RequestHandler): @asynchronous @gen.engine def get(self): http_client = AsyncHTTPClient() http_client.fetch("http://example.com", callback=(yield gen.Callback("key")) response = yield gen.Wait("key") do_something_with_response(response) self.render("template.html") 

All the above examples were taken from the module tornado.gen
')
An example of how to use it with asyncmongo
 class MainHandler(tornado.web.RequestHandler): @property def db(self): if not hasattr(self, '_db'): self._db = asyncmongo.Client(pool_id='mydb', host='127.0.0.1', \ port=27017, maxcached=10, maxconnections=50, dbname='test') return self._db @web.asynchronous @gen.engine def get(self): r, error = yield gen.Task(self.db.user.save, { 'login':'tester' }) r, error = yield gen.Task(self.db.user.find_one, {}) self.write(str(r[0])) self.finish() 

Here, the save and find_one methods are called in turn, with the result.

For the following example, I made a crutch wrapper, so that using mongodb was similar to the classic one (db.user.save ({'login': 'tester'}), db.user.find_one ({}))
 class MainHandler(tornado.web.RequestHandler): @property def db(self): if not hasattr(self, '_db'): self._db = tornadomongo.mongo_client(pool_id='mydb', host='127.0.0.1', port=27017, maxcached=10, maxconnections=50, dbname='test') return self._db @web.asynchronous @gen.engine def get(self): # save r = yield self.db.user.save({ 'login':'tester' }) # find try: r = yield self.db.user.find_one({}) self.write(str(r)) except tornadomongo.MongoError as e: self.write('error: '+str(e)) self.finish() 

Unlike the previous example, this does not need to check the returned error value each time; an exception will work instead. (Although again, you need to check for an exception :)

Who wants to try, the tornadomongo module is here: hg clone bitbucket.org/lega911/tornadomongo
It had to make a “dirty inject” into the tornado module, in order to be able to create an exception, which allows this module to be used only for experiments.

I have already translated some modules of one project to tornado.gen, as a result, the amount of code has decreased, the readability of the code has increased (of the same functionality).

PS: At the time of this writing, the module was not described in the documentation, now posted: www.tornadoweb.org/documentation/gen.html

Source: https://habr.com/ru/post/128982/


All Articles