Понимание ограничений пула и подключения aiohttp.TCPConnector - PullRequest
0 голосов
/ 28 декабря 2018

Я экспериментирую с параметрами limit и limit_per_host для aiohttp.connector.TCPConnector.

. В приведенном ниже сценарии я передаю connector = aiohttp.connector.TCPConnector(limit=25, limit_per_host=5) в aiohttp.ClientSession, затем открываю 2 запроса в docs.aiohttp..org и 3 на github.com.

Результат session.request является экземпляром aiohttp.ClientResponse, и в этом примере я намеренно не вызываю .close() для него, либо через .close(), либо__aexit__.Я бы предположил, что это оставит пул соединений открытым и уменьшит количество доступных подключений к этому (host, ssl, port) в три раза на -1.

Таблица ниже представляет ._available_connections() после каждого запроса. Почему число висит на 4 даже после выполнения 2-го запроса к docs.aiohttp.org? Предположительно, оба эти соединения все еще открыты и еще не получили доступ к ._content или были закрыты.Разве доступные соединения не должны уменьшаться на 1?

After Request Num.        To                    _available_connections
1                         docs.aiohttp.org      4
2                         docs.aiohttp.org      4   <--- Why?
3                         github.com            4
4                         github.com            3
5                         github.com            2

Кроме того, , почему ._acquired_per_host содержит только 1 ключ? Полагаю, я понимаю методы TCPConnector;что объясняет поведение выше?

Полный сценарий:

import aiohttp


async def main():
    connector = aiohttp.connector.TCPConnector(limit=25, limit_per_host=5)

    print("Connector arguments:")
    print("_limit:", connector._limit)
    print("_limit_per_host:", connector._limit_per_host)
    print("-" * 70, end="\n\n")

    async with aiohttp.client.ClientSession(
        connector=connector,
        headers={"User-Agent": "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2225.0 Safari/537.36"},
        raise_for_status=True
    ) as session:

        # Make 2 connections to docs.aiohttp.org and 
        #      3 connections to github.com
        #
        # Note that these instances intentionally do not use
        # .close(), either explicitly or via __aexit__
        # in an async with block

        r1 = await session.request(
            "GET",
            "https://docs.aiohttp.org/en/stable/client_reference.html#connectors"
        )
        print_connector_attrs("r1", session)

        r2 = await session.request(
            "GET",
            "https://docs.aiohttp.org/en/stable/index.html"
        )
        print_connector_attrs("r2", session)

        r3 = await session.request(
            "GET",
            "https://github.com/aio-libs/aiohttp/blob/master/aiohttp/client.py"
        )
        print_connector_attrs("r3", session)

        r4 = await session.request(
            "GET",
            "https://github.com/python/cpython/blob/3.7/Lib/typing.py"
        )
        print_connector_attrs("r4", session)

        r5 = await session.request(
            "GET",
            "https://github.com/aio-libs/aiohttp"
        )
        print_connector_attrs("r5", session)


def print_connector_attrs(name: str, session: aiohttp.client.ClientSession):
    print("Connection attributes for", name, end="\n\n")
    conn = session._connector
    print("_conns:", conn._conns, end="\n\n")
    print("_acquired:", conn._acquired, end="\n\n")
    print("_acquired_per_host:", conn._acquired_per_host, end="\n\n")
    print("_available_connections:")
    for k in conn._acquired_per_host:
        print("\t", k, conn._available_connections(k))
    print("-" * 70, end="\n\n")


if __name__ == "__main__":
    import asyncio
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

Вывод вставляется в https://pastebin.com/rvfzMTe3. Я поставил его там, а не здесь, потому что строки длинные ине очень подходит для переноса.

1 Ответ

0 голосов
/ 02 января 2019

Чтобы ответить на ваш главный вопрос «Почему номер зависает на 4 даже после выполнения 2-го запроса к docs.aiohttp.org?», При вызове aiohttp.connector.BaseConnector._release() количество соединений будет уменьшено, и это будет вызвано, если выдолжны были использовать async with на session.request() или явно вызывать .close() или после прочтения содержимого ответа с помощью .read().Или как в случае запросов docs.aiohttp.org, когда сервер отправляет EOF (это происходит, когда сервер не ждет, пока вы передадите содержимое ответа, а отправит все это в ответ на первый запрос).И это то, что здесь происходит.Вы можете сами убедиться, что если поставить точку останова в aiohttp.connector.BaseConnector._release() и проверить стек, то увидите, что вызывается aiohttp.http_parser.DeflateBuffer.feed_eof().И что aiohttp.streams.StreamReade._eof_callbacks содержит aiohttp.client_reqrep.ClientResponse._response_eof(), что вызывает self._connection.release()

Что касается вашего второго вопроса «почему ._acquired_per_host содержит только 1 ключ», это странно.Но я не вижу ничего об этом в док.Это частный атрибут, поэтому мы не должны связываться с ним.Это, вероятно, просто плохо названный закрытый атрибут.

...