了解 aiohttp。TCPConnector pooling & Connection 限制



我正在对aiohttp.connector.TCPConnectorlimitlimit_per_host参数进行实验。

在下面的脚本中,我将connector = aiohttp.connector.TCPConnector(limit=25, limit_per_host=5)传递给aiohttp.ClientSession,然后向docs.aiohttp.org打开2个请求,向github.com.打开3个请求

session.request的结果是aiohttp.ClientResponse的一个实例,在本例中,我有意不通过.close()__aexit__对其调用.close()。我认为这会使连接池保持打开状态,并将到该池(主机、ssl、端口)的可用连接减少三倍-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只包含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="nn")
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="nn")
conn = session._connector
print("_conns:", conn._conns, end="nn")
print("_acquired:", conn._acquired, end="nn")
print("_acquired_per_host:", conn._acquired_per_host, end="nn")
print("_available_connections:")
for k in conn._acquired_per_host:
print("t", k, conn._available_connections(k))
print("-" * 70, end="nn")

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

输出粘贴在https://pastebin.com/rvfzMTe3.我把它放在那里而不是这里,因为线条很长,不能很好地包裹。

为了解决您的主要问题"为什么在完成对docs.aiohttp.org的第二个请求后,数字仍保持在4?",当调用aiohttp.connector.BaseConnector._release()时,连接计数将减少,如果在session.request()上使用async with,则会调用此计数或者显式调用.close(),或者在使用.read()读取响应内容之后。或者,就像docs.aiohttp.org请求的情况一样,当服务器发送EOF时(当服务器不等待您流式传输响应内容,而是将其全部发送以响应第一个请求时,就会发生这种情况)。这就是这里正在发生的事情。您可以亲眼看到,当您在aiohttp.connector.BaseConnector._release()中放置断点并检查堆栈时,您将看到aiohttp.http_parser.DeflateBuffer.feed_eof()被调用。并且aiohttp.streams.StreamReade._eof_callbacks包含调用self._connection.release()aiohttp.client_reqrep.ClientResponse._response_eof()

至于你的第二个问题"为什么._acquired_per_host只包含一个密钥",这很奇怪。但我在医生那里什么也看不到。这是一个私有属性,所以我们不应该搞砸它。它可能只是名称不正确的私有属性。

最新更新