我有一个特定的用例,在这个用例中,我需要发送一个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
异步的响应?
谢谢
根据@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...
我想出了一个办法,让我的应用程序按照我想要的方式行事(尽快发送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
我觉得不对,本应是一个不常见的用例,却需要一个如此不优雅的解决方案。我是不是错过了什么?
你确定这个任务是在半秒甚至一秒后才运行的吗?因为不应该是这样的,除非循环是 忙碌 和循环不应该忙,除非它是下 重荷 或者你有 屏蔽码 同时运行。你可以使用日志记录来准确检查请求的发送时间和接收时间。
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的值和你看到的很接近,我相信你测量的是完成请求的时间,而不是开始请求的时间。