代码实现
先创建一个基于Flask的server
from flask import Flask
from time import sleep
app = Flask(__name__)
@app.route('/<name>')
def hello_world(name):
sleep(5)
return f'Hello {name}!'
if __name__ == '__main__':
app.run()
client使用asyncio 与 aiohttp进行异步HTTP请求
async def fetch(session, url):
async with session.get(url) as response:
return await response.text()
async def main(name_list):
async with aiohttp.ClientSession() as session:
tasks = [asyncio.ensure_future(fetch(session, f'http://127.0.0.1:5000/{name}')) for name in name_list]
res = await asyncio.gather(*tasks)
print(res)
if __name__ == '__main__':
loop = asyncio.get_event_loop()
name_list = ['a', 'b', 'c']
start = time.time()
loop.run_until_complete(main(name_list))
end = time.time()
print('elapsed time = ' + str(end - start))
loop.close()
先运行server, 然后运行client,client的输出:
['Hello a!', 'Hello b!', 'Hello c!']
elapsed time = 5.019996881484985
可见三个http get请求是异步进行的。
要解释以上的代码,首先介绍一下Python协程
Python协程 (Coroutine)
- 一个例子
借用一篇博客中的例子:
“假设有1个洗衣房,里面有10台洗衣机,有一个洗衣工在负责这10台洗衣机。那么洗衣房就相当于1个进程,洗衣工就相当1个线程。如果有10个洗衣工,就相当于10个线程,1个进程是可以开多线程的。这就是多线程!
那么协程呢?先不急。大家都知道,洗衣机洗衣服是需要等待时间的,如果10个洗衣工,1人负责1台洗衣机,这样效率肯定会提高,但是不觉得浪费资源吗?明明1 个人能做的事,却要10个人来做。只是把衣服放进去,打开开关,就没事做了,等衣服洗好再拿出来就可以了。就算很多人来洗衣服,1个人也足以应付了,开好第一台洗衣机,在等待的时候去开第二台洗衣机,再开第三台,……直到有衣服洗好了,就回来把衣服取出来,接着再取另一台的(哪台洗好先就取哪台,所以协程是无序的)。这就是计算机的协程!洗衣机就是执行的方法。”
- 协程的特点
协程,又称微线程。协程的作用是在执行函数A时可以随时中断去执行函数B,然后中断函数B继续执行函数A(可以自由切换)。但这一过程并不是函数调用,这一整个过程看似像多线程,然而协程只有一个线程执行。
协程很适合处理IO密集型程序的效率问题。协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,因此对于CPU密集型程序协程需要和多进程配合。
asyncio关键字
asyncio是python3.4之后引入的协程模块。关于asyncio的一些关键字的说明:
-
event_loop 事件循环:程序开启一个无限循环,把一些函数注册到事件循环上,当满足事件发生的时候,调用相应的协程函数
-
coroutine 协程:协程对象,指一个使用async关键字定义的函数,它的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环,由事件循环调用。
-
task 任务:一个协程对象就是一个原生可以挂起的函数,任务则是对协程进一步封装,其中包含了任务的各种状态
-
future: 代表将来执行或没有执行的任务的结果。它和task上没有本质上的区别
-
async/await 关键字:python3.5用于定义协程的关键字,async定义一个协程,声明函数挂起的这个切换点,一般是一个耗时的IO操作。
-
run_until_complete方法:将协程包装成为了一个任务(task)对象. task对象是Future类的子类,保存了协程运行后的状态,用于未来获取协程的结果。run_until_complete会运行直到Future结束,返回Future的结果或者抛出exception。
-
asyncio.ensure_future(coroutine):用于创建task
-
asyncio.gather(*tasks):返回coroutine或者futures生成的结果。结果是一个list,顺序和输入的顺序一致,与返回结果的顺序无关。
详细语法可参考官网:https://docs.python.org/3/library/asyncio-task.html#coroutines
aiohttp
aiohttp是一个基于asyncio的异步HTTP client和server, 在python3.5之后加入
详细的语法可以参考官网:https://docs.aiohttp.org/en/stable/index.html
网友评论