python中在使用asyncio中使用requests

起因

需要写个爬虫去爬一些数据,于是用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 asyncio
import time
import requests

@asyncio.coroutine
def 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 asyncio
import requests

async 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

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值