前言
记录一下使用 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 自带的 AsyncHTTPClient,fetch 方法是一个协程函数, 和 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 代码, 之后会开始试着阅读一下源码, 近期频繁写文章, 逼着自己忙一些吧
来源: https://juejin.im/entry/5ab219426fb9a028c368acbf