Tornado基于Epoll(unix为kqueue)的异步网络IO,Tornado的异步包括两个方面,异步服务端和异步客户端。无论服务端和客户端,具体的异步模型又可以分为回调(callback)和协程(coroutine)
@tornado.web.asynchronous 装饰器适用于callback-style的异步方法,如果是协程则可以用@tornado.gen.coroutine来修饰。
对于用@tornado.web.asynchronous 修饰的异步方法,需要主动self.finish()来结束该请求,普通的方法(get()等)会自动结束请求在方法返回的时候。
服务端异步方式:有两种,一种是yield挂起函数,另外一种就是使用类线程池的方式 还有一种Future
1、yield:挂起函数协程,尽管没有block主线程,因为需要处理返回值,挂起到响应执行还是有时间等待
1 class AsyncTaskHandler(tornado.web.RequestHandler):
2 @tornado.web.asynchronous
3 @tornado.gen.coroutine
4 def get(self, *args, **kwargs):
5 # yield 结果
6 response = yield tornado.gen.Task(self.ping, ' www.google.com')
7 print 'response', response
8 self.finish('hello')
9
10 @tornado.gen.coroutine
11 def ping(self, url):
12 os.system("ping -c 2 {}".format(url))
13 return 'after'
2、线程池:
1 from concurrent.futures import ThreadPoolExecutor
2
3 class FutureHandler(tornado.web.RequestHandler):
4 executor = ThreadPoolExecutor(10)
5
6 @tornado.web.asynchronous
7 @tornado.gen.coroutine
8 def get(self, *args, **kwargs):
9
10 url = 'www.google.com'
11 tornado.ioloop.IOLoop.instance().add_callback(functools.partial(self.ping, url))
12 self.finish('It works')
13
14 @tornado.concurrent.run_on_executor
15 def ping(self, url):
16 os.system("ping -c 2 {}".format(url))
要返回值也很容易。再切换一下使用方式接口。使用tornado的gen模块下的with_timeout功能(这个功能必须在tornado>3.2的版本)。
1 class Executor(ThreadPoolExecutor):
2 _instance = None
3
4 def __new__(cls, *args, **kwargs):
5 if not getattr(cls, '_instance', None):
6 cls._instance = ThreadPoolExecutor(max_workers=10)
7 return cls._instance
8
9
10 class FutureResponseHandler(tornado.web.RequestHandler):
11 executor = Executor()
12
13 @tornado.web.asynchronous
14 @tornado.gen.coroutine
15 def get(self, *args, **kwargs):
16
17 future = Executor().submit(self.ping, 'www.google.com')
18
19 response = yield tornado.gen.with_timeout(datetime.timedelta(10), future,
20 quiet_exceptions=tornado.gen.TimeoutError)
21
22 if response:
23 print 'response', response.result()
24
25 @tornado.concurrent.run_on_executor
26 def ping(self, url):
27 os.system("ping -c 1 {}".format(url))
28 return 'after
Future:当发送GET请求时,由于方法被@gen.coroutine装饰且yield 一个 Future对象,那么Tornado会等待,等待用户向future对象中放置数据或者发送信号,如果获取到数据或信号之后,就开始执行done方法。异步非阻塞体现在当在Tornaod等待用户向future对象中放置数据时,还可以处理其他请求。
注意:在等待用户向future对象中放置数据或信号时,此连接是不断开的。
1 import tornado.ioloop
2 import tornado.web
3 from tornado import gen
4 from tornado.concurrent import Future
5
6 future = None
7 class MainHandler(tornado.web.RequestHandler):
8 @gen.coroutine
9 def get(self):
10 global future
11 future = Future()
12 future.add_done_callback(self.done)
13 yield future
14
15 def done(self, *args, **kwargs):
16 self.write('Main')
17 self.finish()
18
19 class IndexHandler(tornado.web.RequestHandler):
20 def get(self):
21 global future
22 future.set_result(None)
23 self.write("Index")
24
25 application = tornado.web.Application([
26 (r"/main", MainHandler),
27 (r"/index", IndexHandler),
28 ])
29
30 if __name__ == "__main__":
31 application.listen(8888)
32 tornado.ioloop.IOLoop.instance().start() #启动 IOLoop 的实例,启动事件循环机制,配合非阻塞的 HTTP Server 工作