我正在用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())
1条答案
按热度按时间l7wslrjt1#
解决方案是将同步代码 Package 在线程中并以这种方式运行它。我使用了这个系统来让我的
asyncio
代码运行boto3
(注意:remove inline type-hints if running < python3.6):请注意,这是因为
s3.get_object()
代码中的大量时间都花在等待I/O上,并且(通常)在等待I/O时,Python会释放GIL(GIL是Python中的线程通常不是一个好主意的原因)。run_in_executor
中的第一个参数None
意味着我们在默认执行器中运行。这是一个线程池执行器,但显式地分配线程池执行器可能会使事情变得更显式。请注意,在使用纯异步I/O的情况下,您可以轻松地同时打开数千个连接,而使用线程池执行器意味着每个并发调用API都需要一个单独的线程。一旦池中的线程用完,线程池将不会调度您的新调用,直到有可用的线程。显然可以提高线程数,但这会消耗内存;别指望能超过几千。
另请参阅python ThreadPoolExecutor文档,了解如何在异步代码中 Package 同步调用的解释和一些略有不同的代码。