python 协程并发发送请求:asyncio、aiohttp 使用

1. 区分并发与并行

并发与并行最主要的区别是:并发在同一时间只能执行一个任务,并行可以执行多个任务。

举例来说,优酷和爱奇艺都可以看电影,现在有电影 A 和电影 B:
    并发:只用优酷或爱奇艺播放电影,不能同时播放电影 A 和电影 B;
    并行:同时使用优酷和爱奇艺,可以同时播放电影 A 和电影 B。

常用的并发方式有协程和线程(协程也叫微线程,需要 python 并发时使用协程就好)
我们平时说 “多线程下载” 可以大大加快下载速度,主要是由于:
    客户端(自己的电脑)发送下载请求后,服务器(网站)需要一段时间才能将请求的数据发送到客户端,中间有一段空白时间;
    多线程下载时,将整个下载任务分为几个小任务,顺序发送各个小任务的数据请求,从而使等待服务器返回数据的时间有一定重合,从而减少整体等待时间,整体来看,就是加快了下载速度。

如下图:
注:数据量较大时(如 1GB 的视频),单线程下载时也并不是只发送一次请求,而是分成很多次,在客户端返回上一次请求的数据后再进行下一次请求。
注:多线程下载时,每个任务等待时间的开始时刻不一致,是因为在任务 1 发送请求处于等待时间内时,将任务 1 挂起,发送任务 2 的请求,以此类推……

2. 使用协程 asyncio 库

2.1. 简介

在使用协程之间,首先需可等待对象的概念。
可等待对象指可以在 await 语句中使用的对象;
执行多个协程任务时,如果遇见可等待对象,就可以将当前任务挂起,执行另一个任务。

如下图,图中 函数f_ 中并没有可等待对象,使用协程消耗的时间与不使用协程相同,大约为 4s。
从输出结果看,可知执行完毕 task_1 之后才开始执行 task_2。


如下图,图中 函数f_ 中含有可等待对象 asyncio.sleep(2),使用协程消耗的时间与不使用协程相同,大约为 2s。
从输出结果看,可知执行 task_1 遇到可等待对象则挂起 task_1,开始执行 task_2。

不含可等待对象的代码:

import asyncio
import time

async def f_(string: str):
    print(f'START {string}')
    time.sleep(2)
    print(f'END {string}')

async def main():
    start = time.time()

    task_1 = f_('coroutine_1')
    task_2 = f_('coroutine_2')
    await asyncio.gather(task_1, task_2)

    end = time.time()

    print(f'time: {end-start}')

asyncio.run(main())

含可等待对象的代码:

import asyncio
import time

async def f_(string: str):
    print(f'START {string}')
    await asyncio.sleep(2)
    print(f'END {string}')

async def main():
    start = time.time()

    task_1 = f_('coroutine_1')
    task_2 = f_('coroutine_2')
    await asyncio.gather(task_1, task_2)

    end = time.time()

    print(f'time: {end-start}')

asyncio.run(main())

2.2. 使用协程

协程函数的定义:使用 “async def function_name(args): ...” 的方式定义

协程执行的方式,根据是否在协程函数定义中,可分为有两类:
    在普通函数定义中(即 `def function_name(args): ...`):
    可使用 `asyncio.run(coroutine_name(args))` 执行,如上图中的 `asyncio.run(main())`
  (asyncio.run 这个函数可以自动创建事件循环,并在协程执行完毕后关闭事件循环,不需要像以前的版本一样手动创建可关闭事件循环)
    在协程函数定义中(即 `async def function_name(args): ...`):
    可使用 await 关键字执行单个协程,
    使用 `await asyncio.gather(task, task, ...)` 形式并发执行多个协程。

async def f():
    await coroutine  # 运行单个协程
    await asyncio.gather(task, task, ...)  # 并发执行多个任务

3. 使用异步请求库 aiohttp

requests 库为同步请求库,不能搭配 asyncio 并发发送请求;
aiohttp 库为异步请求库,非常适合与 asyncio 搭配,并发发送请求。

aiohttp 库用法与 requests 库相似度很高,这里仅介绍 get 请求函数:

async def declare_coroutine():
    async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(10)) as session:
        async with session.get(url=str, params=dict) as resp:
            resp.status # 响应状态码
            resp.headers # 响应头
            await resp.text() # 响应内容(文本格式)
            await resp.read() # 响应内容(二进制格式)
  • 20
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值