最大化并行请求数(aiohttp)

Maximize number of parallel requests (aiohttp)

tl;dr:如何最大化可以并行发送的 http 请求数?

我正在使用 aiohttp 库从多个 url 中获取数据。我正在测试它的性能,我发现在这个过程中的某个地方存在瓶颈,运行一次 urls 只是无济于事。

我正在使用此代码:

import asyncio
import aiohttp

async def fetch(url, session):
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0'}
    try:
        async with session.get(
            url, headers=headers, 
            ssl = False, 
            timeout = aiohttp.ClientTimeout(
                total=None, 
                sock_connect = 10, 
                sock_read = 10
            )
        ) as response:
            content = await response.read()
            return (url, 'OK', content)
    except Exception as e:
        print(e)
        return (url, 'ERROR', str(e))

async def run(url_list):
    tasks = []
    async with aiohttp.ClientSession() as session:
        for url in url_list:
            task = asyncio.ensure_future(fetch(url, session))
            tasks.append(task)
        responses = asyncio.gather(*tasks)
        await responses
    return responses

loop = asyncio.get_event_loop()
asyncio.set_event_loop(loop)
task = asyncio.ensure_future(run(url_list))
loop.run_until_complete(task)
result = task.result().result()

运行 这具有不同长度的 url_list(针对 https://httpbin.org/delay/2 的测试)我看到添加更多 urls一次 运行 最多只能帮助 ~100 urls 然后总时间开始与 urls 的数量成比例增长(或者换句话说,每个 url 不减少)。这表明在尝试一次处理这些时有些东西失败了。此外,在 'one batch' 中有更多 urls,我偶尔会收到连接超时错误。

我运行在 Windows 上对此表示不满。

编辑 回复评论:

这是限制设置为 None 的相同数据。最后只有轻微的改善,并且有很多连接超时错误,一次发送 400 urls。我最终在实际数据上使用了 limit = 200

默认情况下 aiohttp 将同时连接数限制为 100。它通过将默认 limit 设置为 TCPConnector object 来实现,ClientSession 使用。您可以通过创建自定义连接器并将其传递给会话来绕过它:

connector = aiohttp.TCPConnector(limit=None)
async with aiohttp.ClientSession(connector=connector) as session:
    # ...

但是请注意,您可能不想将此数字设置得太高:您的网络容量、CPU、RAM 和目标服务器都有其自身的限制,尝试建立大量连接可能会导致越来越多的失败。

最好的数字大概只能通过具体的机器实验才能找到。


无关:

没有 ,您不必创建任务。大多数 asyncio api 接受常规协程。例如,您的最后几行代码可以这样更改:

loop = asyncio.get_event_loop()
loop.run_until_complete(run(url_list))

如果您使用 Python 3.7

,甚至只是 asyncio.run(run(url_list)) (doc)