~玉米糊~
慢慢来,也会很快。 非宁静无以志学,学什么都一样,慢慢打基础,找规律、认真、坚持,其余的交给时间。
随笔 - 117,  文章 - 17,  评论 - 1,  阅读 - 82072

前言

如果需要并发 http 请求怎么办呢?requests库是同步阻塞的,必须等到结果才会发第二个请求,这里需使用http请求异步库 aiohttp。

环境准备

aiohttp 用于 asyncio 和 Python 的异步 HTTP 客户端/服务器。
使用pip安装对应的包。当前使用版本v3.8.1

1
pip install aiohttp

  

并发http请求

如果使用requests 库,发10个请求访问我的博客,那么这10个请求是串行的。

复制代码
 1 import requests
 2 import time
 3 
 4 url = "https://www.cnblogs.com/yoyoketang/"
 5 
 6 start_time = time.time()
 7 for i in range(10):
 8     r = requests.get(url)
 9     print(r)
10 print('总耗时:', time.time()-start_time)
复制代码

 

运行结果,总共耗时1秒左右

<Response [200]>
<Response [200]>
<Response [200]>
<Response [200]>
<Response [200]>
<Response [200]>
<Response [200]>
<Response [200]>
<Response [200]>
<Response [200]>
总耗时:1.0870192050933838

使用asyncio + aiohttp 并发请求

复制代码
import asyncio
from aiohttp import ClientSession
import time

async def bai_du(url):
    print(f'启动时间: {time.time()}')
    async with ClientSession() as session:
        async with session.get(url) as response:
            res = await response.text()
            return res

async def main():
    url = "https://www.cnblogs.com/yoyoketang/"
    task_list = []
    for i in range(10):
        task = asyncio.create_task(bai_du(url))
        task_list.append(task)
    done, pending = await asyncio.wait(task_list, timeout=None)
    # 得到执行结果
    for done_task in done:
        print(f"{time.time()} 得到执行结果 {done_task.result()}")

# asyncio.run(main())
start_time = time.time()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
print("总耗时: ", time.time()-start_time)
复制代码

 

运行结果

启动时间: 1646028822.3952098
启动时间: 1646028822.4161537
启动时间: 1646028822.4171515
启动时间: 1646028822.4171515
启动时间: 1646028822.4171515
启动时间: 1646028822.4171515
启动时间: 1646028822.4181483
启动时间: 1646028822.4181483
启动时间: 1646028822.4181483
启动时间: 1646028822.4181483
....
总耗时: 0.17400002479553223

从运行结果可以看到,启动时间非常接近,也就是并发的请求,总过耗时 0.17 秒。

asyncio.run

需注意的是这里使用 asyncio.run(main()) 会报错RuntimeError: Event loop is closed

Exception ignored in: <function _ProactorBasePipeTransport.__del__ at 0x00000238675A8F70>
Traceback (most recent call last):
File "D:\python3.8\lib\asyncio\proactor_events.py", line 116, in __del__
self.close()
File "D:\python3.8\lib\asyncio\proactor_events.py", line 108, in close
self._loop.call_soon(self._call_connection_lost, None)
File "D:\python3.8\lib\asyncio\base_events.py", line 719, in call_soon
self._check_closed()
File "D:\python3.8\lib\asyncio\base_events.py", line 508, in _check_closed
raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed

解决办法,把执行方式 asyncio.run(main())改成

# asyncio.run(main())
loop = asyncio.get_event_loop()
loop.run_until_complete(main())

注意原因是asyncio.run()会自动关闭循环,并且调用ProactorBasePipeTransport._del报错, 而asyncio.run_until_complete()不会.
详情参考https://zhuanlan.zhihu.com/p/365815189

posted on   yuminhu  阅读(368)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示