aiohttp:将请求发送和响应等待分开。

问题描述 投票:0回答:1

我有一个特定的用例,在这个用例中,我需要发送一个HTTP请求。ASAP但不能等HTTP响应回来再做一些必要的工作。

在概念上,我需要这样做。

async with aiohttp.ClientSession() as session:
    request = await session.send_request(method='GET', url='https://httpbin.org/get')
    # do some necessary work
    response = await request.get_response()
    # process response...

简单明了的方法的问题是 我可以尽快发送HTTP请求 但在等待响应的过程中我不能让步

async with aiohttp.ClientSession() as session:
    # this blocks until both the request is sent AND response arrived
    response = await session.request(method='GET', url='https://httpbin.org/get')
    # process response...

我试着旋转一个新的coroutine 这样就不用等待HTTP响应的到来了:

async def foo(url):
    async with aiohttp.ClientSession() as session:
         response = await session.request(method='GET', url=url)
         # process response...

asyncio.create_task(foo('https://httpbin.org/get'))
# do necessary work

但后来由于... create_task() 发生在 "事件循环的第一次机会",有时是在我调用了 create_task()这对我的目的来说太慢了。

我的问题是

(a) 有没有办法将HTTP请求的发送和HTTP响应的等待分开?aiohttp?

(b)如果没有,你能不能建议一种替代的方式来发送HTTP请求ASAP,但要到 await 异步的响应?

谢谢

更新#1

根据@Isabi在评论中的建议,我尝试只使用 await 在完成必要的工作后,但HTTP请求永远不会被发送,直到 await 被使用,例如。

async with aiohttp.ClientSession() as session:
    # send out an HTTP request that takes ~2 seconds before response comes back
    request = session.request(method='GET', url='https://httpbin.org/delay/2')
    await asyncio.sleep(4)  # simulate 4 seconds of necessary work
    # the following line still takes 2 seconds, indicating the request
    # didnt go out before `await` is used
    response = await request
    # process response...

更新#2

我想出了一个办法,让我的应用程序按照我想要的方式行事(尽快发送HTTP请求,但不要阻止等待HTTP响应)。该解决方案使用了对 asyncio.sleep(0)灵感来自 这条. 然而,它在美学上并不令人满意。

async def foo(url):
    async with aiohttp.ClientSession() as session:
         response = await session.request(method='GET', url=url)
         # process response...

asyncio.create_task(foo('https://httpbin.org/get'))
await asyncio.sleep(0)
# do necessary work

我觉得不对,本应是一个不常见的用例,却需要一个如此不优雅的解决方案。我是不是错过了什么?

python-asyncio aiohttp
1个回答
0
投票

你确定这个任务是在半秒甚至一秒后才运行的吗?因为不应该是这样的,除非循环是 忙碌 和循环不应该忙,除非它是下 重荷 或者你有 屏蔽码 同时运行。你可以使用日志记录来准确检查请求的发送时间和接收时间。

import asyncio
import aiohttp
import logging

logging.basicConfig(format="%(asctime)s.%(msecs)03d %(levelname)s %(message)s", datefmt="%Y-%m-%d %H:%M:%S", level=logging.INFO)


async def foo(url):
    async with aiohttp.ClientSession() as session:
         logging.info("request started")
         response = await session.request(method="GET", url=url)
         logging.info("response received")
         text = await response.text()
         logging.info(f"response read {len(text)} bytes")
         # process response...


async def test():
    logging.info("sleep 0.1")
    await asyncio.sleep(0.1)
    logging.info("create task")
    asyncio.create_task(foo("https://httpbin.org/get"))
    logging.info("task created, sleep 2")
    await asyncio.sleep(2)
    logging.info("finished")


if __name__== "__main__":
    asyncio.get_event_loop().run_until_complete(test())

输出:

2020-06-10 10:52:00.017 INFO sleep 0.1
2020-06-10 10:52:00.118 INFO create task
2020-06-10 10:52:00.118 INFO task created, sleep 2
2020-06-10 10:52:00.119 INFO request started
2020-06-10 10:52:00.621 INFO response received
2020-06-10 10:52:00.622 INFO response read 308 bytes
2020-06-10 10:52:02.121 INFO finished

注意到coroutine开始运行的时间 1毫秒 创建后,HTTP请求大约需要0.5秒才能完成;因为0.5的值和你看到的很接近,我相信你测量的是完成请求的时间,而不是开始请求的时间。

© www.soinside.com 2019 - 2024. All rights reserved.