将Web响应写到异步程序中的文件

时间:2019-03-08 00:23:48

标签: python python-3.x asynchronous python-asyncio aiohttp

正在努力将使用ThreadPoolExecutors的服务器查询工具的实现替换为使用 try { assertTrue(hm.isPrime(-37337)); } catch (AssertionError e ) { e.printStackTrace(); } asyncio的所有异步调用。由于网络调用是无阻塞的IO,因此大多数过渡都是直接的,保存响应使我感到困惑。

我正在使用的所有示例,甚至包括两个库的文档,都使用aiohttp来收集所有等待的结果。就我而言,这些结果可以是许多GB范围内的文件,我不想将它们存储在内存中。

什么是解决此问题的合适方法?是否要使用asyncio.as_completed(),然后:

asyncio.gather()

这不是引入线程吗(假设默认情况下我使用for f in as_completed(aws): earliest_result = await f # Assumes `loop` defined under `if __name__` block outside coroutine loop = get_event_loop() # Run the blocking IO in an exectuor and write to file _ = await loop.run_in_executor(None, save_result, earliest_result) ),从而使它成为异步的多线程程序,而不是异步的单线程程序?

还有,这是否可以确保在任何时候仅将1个ThreadPoolExecutor写入文件?我不希望对earliest_result的调用正在运行,然后出现另一个结果,我尝试运行到同一文件;我想可以限制一个信号量。

2 个答案:

答案 0 :(得分:1)

我建议使用aiohttp Streaming API。将您的响应直接写到磁盘而不是RAM上,并返回文件名而不是收集到响应本身。这样做根本不会占用太多内存。这是我的意思的一个小演示:

import asyncio

import aiofiles
from aiohttp import ClientSession


async def make_request(session, url):
    response = await session.request(method="GET", url=url)
    filename = url.split('/')[-1]
    async for data in response.content.iter_chunked(1024):
        async with aiofiles.open(filename, "ba") as f:
            await f.write(data)
    return filename


async def main():
    urls = ['https://github.com/Tinche/aiofiles',
            'https://github.com/aio-libs/aiohttp']
    async with ClientSession() as session:
        coros = [make_request(session, url) for url in urls]
        result_files = await asyncio.gather(*coros)
    print(result_files)


asyncio.run(main())

答案 1 :(得分:0)

  

就我而言,这些结果可能是许多GB范围内的文件,我不想将它们存储在内存中。

如果我是正确的,并且在您的代码中单个aws意味着一个文件的下载,您可能会遇到以下问题:尽管as_completed允许尽快将RAM中的数据交换到HDD您的aws并行运行,同时将每个数据(带有部分下载文件的缓冲区)同时存储在RAM中。

要避免这种情况,您首先需要使用信号量以确保并行下载的文件不多,从而防止RAM过度使用。

这里是使用semaphore的示例。

  

这不是引入线程吗(假设我使用ThreadPoolExecutor   默认情况下),因此使它成为异步的多线程程序   异步的单线程程序?

我不确定,我理解您的问题,但是是的,您的代码将使用线程,但是在这些线程中仅执行save_result。所有其他代码仍在单个主线程中运行。这里没什么问题。

  

还有,这是否确保只写入1个earlyest_result   随时归档吗?

是,是[*]。准确地说,在代码段的最后一行使用关键字await可以确保:

_ = await loop.run_in_executor(None, save_result, earliest_result)

您可以将其读取为:“开始异步执行run_in_executor,并在此行暂停执行流程,直到run_in_executor完成并返回结果为止。”


[*]是的,如果您没有为f in as_completed(aws)循环运行多个循环。