Tornado异步与延迟任务

最近一直在研究Tornado异步操作,然而一番研究后发现要使一个函数异步化的最好方法就是采用相关异步库,但目前很多功能强大的库都不在此列。经过一番查找文档和搜索示范,终于发现了ThreadPoolExecutor模块和run_on_executor装饰器。用法就是建立线程池,用run_on_executor装饰的函数即运行在其中线程中,从而从主线程中分离出来,达到异步的目的。 另外,Tornado的IOLoop实例还有IOLoop.add_callback(callback, *args, **kwargs)方法,文档中的描述如下:

Calls the given callback on the next I/O loop iteration. It is safe to call this method from any thread at any time, except from a signal handler. Note that this is the only method in IOLoop that makes this thread-safety guarantee; all other interaction with the IOLoop must be done from that IOLoop‘s thread. add_callback() may be used to transfer control from other threads to the IOLoop‘s thread.

意思就是在执行add_callback方法后马上就会执行下一行代码,而callback函数将在下一轮事件循环中才调用,从而就能实现延迟任务。在Web APP中应付HTTP请求时,当有一些耗时操作并不需要返回给请求方时,就可以采用延迟任务的形式,比如发送提醒邮件。 示范代码如下:

#!/bin/env python
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
import tornado.httpclient
import tornado.gen
from tornado.concurrent import run_on_executor
# 这个并发库在python3自带;在python2需要安装sudo pip install futures
from concurrent.futures import ThreadPoolExecutor
import time
from tornado.options import define, options
define("port", default=8002, help="run on the given port", type=int)

class SleepHandler(tornado.web.RequestHandler):
    executor = ThreadPoolExecutor(2)

    def get(self):
        tornado.ioloop.IOLoop.instance().add_callback(self.sleep)       # 这样将在下一轮事件循环执行self.sleep
        self.write("when i sleep")

    @run_on_executor
    def sleep(self):
        time.sleep(5)
        print("yes")
        return 5


if __name__ == "__main__":
    tornado.options.parse_command_line()
    app = tornado.web.Application(handlers=[
            (r"/sleep", SleepHandler), ])
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()