tornado异步编程

4,319 阅读3分钟
原文链接: yangsoon.github.io

前言

记录一下使用tornado进行异步编程的简单代码,如果你看过我之前写的关于asyncio的文章你会发现使用tornado和使用asyncio的模式大致上一样。talk is easy, show you the code。下面的demo大部分都来自官方文档,部分进行了修改。

demo

1. 同步网络请求

第一个demo是使用tornado进行同步编程的例子,这里是为了和下面的异步版本进行比较,我们使用tornado自带的同步网络请求来进行测试。例子很简单,没有什么要说明的,其中结果显示,使用了一个回调函数,请求结束后会调用回调函数,输出结果。

from tornado.httpclient import HTTPClient
from time import strftime
urls = [
    'http://163.com',
    'http://baidu.com'
]
def sync_fetch(url ,callback):
    def handler_result(response):
        callback(response)
    
    http_client = HTTPClient()
    http_client.fetch(url, callback=handler_result)
def sync_fetch_callback(response):
    print(strftime('[%H:%M:%S]'),end=' ')
    print('url:{} state:{} time:{}'.format(
        response.request.url, response.code, response.request_time))
if __name__ == "__main__":
    for url in urls:
        sync_fetch(url, sync_fetch_callback)

输出结果

[16:11:43] url:http://163.com state:200 time:0.15146231651306152
[16:11:43] url:http://baidu.com state:200 time:0.06663918495178223

2.异步网络请求

下面的demo的一个异步版本,使用了tornado自带的AsyncHTTPClientfetch方法是一个协程函数,和asyncio一样,协程函数需要用@gen.coroutine装饰,使用yield等待请求结果返回。但是不会阻塞主线程,其中yield list会并行等待list中的元素。tornado.ioloop.IOLoop.current()会获取到当前的事件循环对象,loop.run_sync(main)会在main执行完成后,关闭事件循环。是不是和asyncio和很像,有没有,有没有?通过和上面的输出结果对比,可以发现因为访问baidu更快,所以最先输出访问baidu的结果。

import tornado
from tornado import gen
from time import strftime
from tornado.httpclient import AsyncHTTPClient
urls = [
    'http://163.com',
    'http://baidu.com'
]
def format_print(response):
    print(strftime('[%H:%M:%S]'),end=' ')
    print('url:{} state:{} time:{}'.format(
        response.request.url, response.code, response.request_time))
@gen.coroutine
def async_fetch(url):
    http_client = AsyncHTTPClient()
    res = yield http_client.fetch(url)
    format_print(res)
@gen.coroutine
def main():
    yield [async_fetch(url) for url in urls]
if __name__ == '__main__':
    loop = tornado.ioloop.IOLoop.current()
    loop.run_sync(main)

输出结果

[16:13:16] url:http://baidu.com state:200 time:0.06943202018737793
[16:13:16] url:http://163.com state:200 time:0.15732026100158691

经典demo

为什么说是经典demo呢,因为之前的asyncio中的demo都是使用阻塞或者非阻塞的sleep函数来模拟实际的函数操作。和asyncio一样,tornado提供了不阻塞的sleep函数 gen.sleep。下面的例子和之前asyncio中的例子基本一样,所以就不进行说明了。

from time import strftime
from tornado.ioloop import IOLoop
from tornado import gen
@gen.coroutine
def noblock_sleep(t):
    print(strftime('[%H:%M:%S]'), end=' ')
    print('func {} is running {}s'.format(t, t))
    yield gen.sleep(t)
    print(strftime('[%H:%M:%S]'), end=' ')
    print('func {} is finished'.format(t))
@gen.coroutine
def main():
    yield [noblock_sleep(t) for t in range(1,6)]
if __name__ == '__main__':
    loop = IOLoop.current()
    loop.run_sync(main)

输出结果

[16:24:12] func 1 is running 1s
[16:24:12] func 2 is running 2s
[16:24:12] func 3 is running 3s
[16:24:12] func 4 is running 4s
[16:24:12] func 5 is running 5s
[16:24:13] func 1 is finished
[16:24:14] func 2 is finished
[16:24:15] func 3 is finished
[16:24:16] func 4 is finished
[16:24:17] func 5 is finished

在tornado中使用阻塞函数

没错,和asyncio一样,tornado提供了loop.run_in_executor执行阻塞操作。直接看代码就好,下面就不赘述了。

from tornado import gen
from time import sleep, strftime
from concurrent.futures import ThreadPoolExecutor
from concurrent import futures
from tornado.ioloop import IOLoop
def blocked(t):
    print(strftime('[%H:%M:%S]'), end=' ')
    print('func {} is running {}s'.format(t, t))
    sleep(t)
    print(strftime('[%H:%M:%S]'), end=' ')
    print('func {} is end'.format(t))
    return t
@gen.coroutine
def main():
    with ThreadPoolExecutor(max_workers=5) as executor:
        loop = IOLoop.current()
        results = yield [loop.run_in_executor(executor, blocked, t) for t in range(1,6)]
        return results
        
if __name__ == '__main__':
    loop = IOLoop.current()
    results = loop.run_sync(main)
    print('results:{}'.format(results))

输出结果

[16:29:15] func 1 is running 1s
[16:29:15] func 2 is running 2s
[16:29:15] func 3 is running 3s
[16:29:15] func 4 is running 4s
[16:29:15] func 5 is running 5s
[16:29:16] func 1 is end
[16:29:17] func 2 is end
[16:29:18] func 3 is end
[16:29:19] func 4 is end
[16:29:20] func 5 is end
results:[1, 2, 3, 4, 5]

最后

这里只是简单的列出一些tornado代码,之后会开始试着阅读一下源码,近期频繁写文章,逼着自己忙一些吧。