python-3.x 将同步请求封装到asyncio(async/await)中?

8mmmxcuj  于 2023-06-25  发布在  Python
关注(0)|答案(1)|浏览(99)

我正在用Python 3.6编写一个工具,它将请求发送到几个API(具有各种端点),并收集它们的响应以解析并保存在数据库中。
我使用的API客户端有一个请求URL的同步版本,例如它们使用
urllib.request.Request('...
或者他们使用Kenneth雷茨的Requests库。
由于我的API调用依赖于请求URL的同步版本,因此整个过程需要几分钟才能完成。
现在我想把我的API调用 Package 在async/await(asyncio)中。我用的是Python 3.6。
我找到的所有示例/教程都希望我将同步URL调用/requests更改为异步版本(例如aiohttp)。由于我的代码依赖于我没有编写的API客户端(我不能更改),因此我需要保持代码不变。

那么,有没有一种方法可以将我的同步请求(阻塞代码) Package 在async/await中,使它们在事件循环中运行?

我是Python中的asyncio新手。这在NodeJS中是显而易见的。但是我不能用Python来理解这个。

更新2023-06-12

下面是我在Python 3.9+中的操作

import asyncio
import requests

async def main():
    response1 = await asyncio.to_thread(requests.get, 'http://httpbin.org/get')
    response2 = await asyncio.to_thread(requests.get, 'https://api.github.com/events')
    print(response1.text)
    print(response2.text)

asyncio.run(main())
l7wslrjt

l7wslrjt1#

解决方案是将同步代码 Package 在线程中并以这种方式运行它。我使用了这个系统来让我的asyncio代码运行boto3(注意:remove inline type-hints if running < python3.6):

async def get(self, key: str) -> bytes:
    s3 = boto3.client("s3")
    loop = asyncio.get_event_loop()
    try:
        response: typing.Mapping = \
            await loop.run_in_executor(  # type: ignore
                None, functools.partial(
                    s3.get_object,
                    Bucket=self.bucket_name,
                    Key=key))
    except botocore.exceptions.ClientError as e:
        if e.response["Error"]["Code"] == "NoSuchKey":
            raise base.KeyNotFoundException(self, key) from e
        elif e.response["Error"]["Code"] == "AccessDenied":
            raise base.AccessDeniedException(self, key) from e
        else:
            raise
    return response["Body"].read()

请注意,这是因为s3.get_object()代码中的大量时间都花在等待I/O上,并且(通常)在等待I/O时,Python会释放GIL(GIL是Python中的线程通常不是一个好主意的原因)。
run_in_executor中的第一个参数None意味着我们在默认执行器中运行。这是一个线程池执行器,但显式地分配线程池执行器可能会使事情变得更显式。
请注意,在使用纯异步I/O的情况下,您可以轻松地同时打开数千个连接,而使用线程池执行器意味着每个并发调用API都需要一个单独的线程。一旦池中的线程用完,线程池将不会调度您的新调用,直到有可用的线程。显然可以提高线程数,但这会消耗内存;别指望能超过几千。
另请参阅python ThreadPoolExecutor文档,了解如何在异步代码中 Package 同步调用的解释和一些略有不同的代码。

相关问题