python中在使用asyncio中使用requests
来源:互联网 发布:js笔试题及答案 编辑:程序博客网 时间:2024/05/29 18:35
起因
需要写个爬虫去爬一些数据,于是用python写了个,但由于众所周知的GIL锁问题,python的多线程其实效率并不高,于是准备采用协程的方法去实现,在写demo测试的时候就遇到问题了,使用await去等待requests的响应却是无效的
测试代码
import asyncio import requests async def hello1(url): print('55555555555555555555') async with requests.get(url) as resp: print(url, resp.status_code) print('666666666666') async def hello(n,url): print("协程" + str(n) +"启动") await hello1(url) print("协程" + str(n) + "结束") if __name__ == "__main__": tasks = [] url = 'http://localhost:8080/TBIMPSWEB/drugPrice/query.tran?REQ_MESSAGE={}' for i in range(0, 3): tasks.append(hello(i,url)) loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.wait(tasks)) loop.close()
希望实现的效果是:
当hello1()执行requests.get请求时,服务器还没响应就先去执行其他的协程的代码,也就是输出55555555555部分,我给服务器上对应controller加了断点,希望得到的效果是协程启动-》输出5555,服务器未响应,切换协程-》协程启动-》输出55555-》协程启动-》输出55555,服务器点继续就输出响应码以及6666666
实际效果是:
第一条协程执行到请求部分就停下来了,等服务器响应,不会切到其他的协程去执行
第一次改进后:
import asyncioimport timeimport requests@asyncio.coroutinedef hello1(url): yield print('55555555555555555555') print('是否执行请求') resp = requests.get(url) print('666666666666')async def hello(n,url): print("协程" + str(n) +"启动") await hello1(url) print("协程" + str(n) + "结束")if __name__ == "__main__": tasks = [] url = 'http://localhost:8080/TBIMPSWEB/drugPrice/query.tran?REQ_MESSAGE={}' for i in range(0, 3): tasks.append(hello(i,url)) loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.wait(tasks)) loop.close()
输出结果为:
协程0启动
55555555555555555555
协程1启动
55555555555555555555
协程2启动
55555555555555555555
是否执行请求
显然每次执行到hello1()中的yield部分返回了,去切换到其他协程,但是下面的请求部分并未执行,这样的话不就等于三个协程都执行完前面的计算部分,然后再依次执行io部分,而不是第一个协程去执行io部分的同时,切到其他协程去执行计算部分
解决方案:
最后在一位前辈的指点下才知道问题,requests会阻塞asyncio循环,所以会出现收不到服务器响应的情况。可以使用aiohttp替代requests解决这个问题。
但是stack overflow给出了一个使用requests会阻塞asyncio的解决方法:
import asyncioimport requestsasync def hello1(url): print('55555555555555555555') loop = asyncio.get_event_loop() await loop.run_in_executor(None, requests.get, url) print('666666666666')async def hello(n,url): print("协程" + str(n) +"启动") await hello1(url) print("协程" + str(n) + "结束") # await hello2(n)if __name__ == "__main__": tasks = [] url = 'http://localhost:8080/TBIMPSWEB/drugPrice/query.tran?REQ_MESSAGE={}' for i in range(0, 100): tasks.append(hello(i,url)) loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.wait(tasks)) loop.close()
测试了下,符合预期,把协程数量设置成100,虽然看上去都是先启动,全部启动完了再去运行结束,但是实际上,在其他协程的启动过程中,后台服务器是一直在接收请求的,也就是能实现挂起io操作转而执行其他线程的效果
参考链接:原问题
以及stackoverflow官网上关于这种问题的解释:
https://stackoverflow.com/questions/22190403/how-could-i-use-requests-in-asyncio
- python中在使用asyncio中使用requests
- Python asyncio使用范例
- python中requests使用代理proxies
- Python 使用 asyncio 包处理并发
- python中的requests使用
- Python-requests的使用
- 在python中使用Yaml
- 在 Python 中使用 ArcObjects
- 在python中使用OpenCV
- 在python中使用websocket
- 在python中使用SQLite
- 在 Python 中使用 ArcObjects
- 在python中使用mysql
- 在python中使用liblinear
- 在Python中使用QuantLib
- 在Python中使用QuantLib
- 在Python中使用XGBoost
- 在python中使用epoll
- macOS high sierra 下搭建redis集群
- Java中long类型为何会自动转换为float类型?(未整理)
- C++虚函数应用
- 网卡类WiFi模块系列三:USB接口双通道单/双频WiFi模块
- 补第十一周Leetcode 博客
- python中在使用asyncio中使用requests
- USB通信协议
- struts2 --- 拦截器
- 中企动力小程序开发服务上市,4大核心功能支持全行业
- 对动态规划DP的深入理解
- Java并发编程规则:判定对象是否存在多线程访问
- 【二进制分组+AC自动机】HDU4787[GRE Words Revenge]题解
- 给c++跪了.cpp
- 【IDEA Intel 输入法】Android Studio ,和 PhpStorm ,修复中文输入法,运用了两套 jre64 工具来解决。