了解aiohttp.TCPConnector的连接池和连接限制

13
我正在尝试使用aiohttp.connector.TCPConnectorlimitlimit_per_host参数进行实验。
在下面的脚本中,我将connector = aiohttp.connector.TCPConnector(limit=25, limit_per_host=5)传递给aiohttp.ClientSession,然后打开2个请求到docs.aiohttp.org和3个请求到github.com。 session.request的结果是aiohttp.ClientResponse的一个实例,在这个例子中,我有意不通过.close()__aexit__调用它。我认为这会保持连接池打开,并将可用连接减少到该(host, ssl, port)三元组-1。
下表表示每个请求后的._available_connections()为什么即使完成了对docs.aiohttp.org的第二个请求,数字仍停留在4?这两个连接都可能仍然打开,并且还没有访问._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只包含一个键?我猜可能是我对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。我将它放在那里,而不是在这里,因为这些行很长且无法很好地换行。

仅凭我的经验,你可以使用aiohttp连接池,但同时也需要考虑连接重用和保持活动状态。后者会影响脚本的计数吗? - ElToro1966
@ElToro1966 我也考虑过查看那里,但似乎默认的保持连接时间是15.0秒。所以在这里并不适用,对吗? - Brad Solomon
1个回答

14
针对你的主要问题“为什么在完成第二个到docs.aiohttp.org的请求后数字仍然保持在4?”,当调用aiohttp.connector.BaseConnector._release()时,连接数将会减少,如果你使用了async with on session.request(),或者显式地调用.close(),或者读取响应内容后调用了.read(),都会调用这个方法。又或者像在docs.aiohttp.org请求中一样,当服务器发送EOF时(当服务器不等待你流式传输响应内容而是在第一个请求中全部发送响应内容时),也会触发调用此方法。这就是这里正在发生的事情。你可以自己看看,在aiohttp.connector.BaseConnector._release()处设置断点并检查堆栈,你将看到aiohttp.http_parser.DeflateBuffer.feed_eof()被调用,以及aiohttp.streams.StreamReader._eof_callbacks包含aiohttp.client_reqrep.ClientResponse._response_eof(),该函数调用self._connection.release()
至于你的第二个问题“为什么._acquired_per_host只有一个键”,这很奇怪。但我在文档中找不到任何关于它的信息。因为它是一个私有属性,所以我们不应该去改动它。这可能只是一个命名不好的私有属性。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接