Python 请求/aiohttp:关闭响应对象
我对在Python 请求/aiohttp:关闭响应对象,python,python-requests,aiohttp,python-3.7,Python,Python Requests,Aiohttp,Python 3.7,我对在请求和aiohttp中都需要.close()响应对象感到有点困惑。(请注意,这是一个独立于session.close()的实例方法——我指的是响应对象本身。) Response(requests)或ClientResponse(aiohttp)是否需要显式调用.close() 如果不是,那么将响应本身用作上下文管理器的目的是什么?(与session.request('GET','https://www.pastebin.com(下面的“)如果它被隐式关闭,为什么要为此定义两个dunder
请求和aiohttp
中都需要.close()
响应对象感到有点困惑。(请注意,这是一个独立于session.close()
的实例方法——我指的是响应对象本身。)
Response
(requests
)或ClientResponse
(aiohttp
)是否需要显式调用.close()
- 如果不是,那么将响应本身用作上下文管理器的目的是什么?(
与session.request('GET','https://www.pastebin.com(下面的“
)如果它被隐式关闭,为什么要为此定义两个dunder方法,如下所示
一些简单的测试(如下)似乎暗示,当响应在会话上下文管理器中定义时,它们会自动关闭。(它本身在\uuuuuu exit\uuuu
或\uuuuuu\uuu
中调用self.close()
。但这是会话的关闭,而不是响应对象。)
示例-请求
示例-aiohttp
请求
:不需要显式调用close()
。请求将在完成后自动关闭,因为它基于urlopen(这就是为什么resp.raw.closed
为True),这是我观看了session.py
和adapters.py之后的简化代码:
from urllib3 import PoolManager
import time
manager = PoolManager(10)
conn = manager.connection_from_host('host1.example.com')
conn2 = manager.connection_from_host('host2.example.com')
res = conn.urlopen(url="http://host1.example.com/",method="get")
print(len(manager.pools))
manager.clear()
print(len(manager.pools))
print(res.closed)
#2
#0
#True
那么,\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
做了什么?它用于清除PoolManager(self.PoolManager=PoolManager(…)
和proxy
# session.py
def __exit__(self, *args): #line 423
self.close()
def close(self): #line 733
for v in self.adapters.values():
v.close()
# adapters.py
# v.close()
def close(self): #line 307
self.poolmanager.clear()
for proxy in self.proxy_manager.values():
proxy.clear()
那么您应该在什么时候使用close()
,正如注释所说的释放回池的连接,因为DEFAULT\u POOLSIZE=10
(http/https是独立的)。这意味着,如果您想在一次会话中访问10个以上的网站,您可以选择关闭一些不需要的网站,否则当您有一个以上的会话时,manager将从第一个网站关闭到最新的网站。但实际上您不需要关心这个问题,您可以指定池大小,并且重建连接不会浪费太多时间
aiohttp
aiohttp.ClientSession()正在为所有请求使用一个TCPConnector。当它触发时,自身连接器将关闭
编辑:s.request()
设置了来自主机的连接,但未收到响应wait resp.text()
只能在得到响应后执行,如果您没有执行此步骤(等待响应),您将在没有响应的情况下退出
if connector is None: #line 132
connector = TCPConnector(loop=loop)
...
self._connector = connector #line 151
# connection timeout
try:
with CeilTimeout(real_timeout.connect,loop=self._loop):
assert self._connector is not None
conn = await self._connector.connect(
req,
traces=traces,
timeout=real_timeout
)
...
async def close(self) -> None:
if not self.closed:
if self._connector is not None and self._connector_owner:
self._connector.close()
self._connector = None
...
async def __aexit__(self,
...) -> None:
await self.close()
这是显示我所说内容的代码
import asyncio
import aiohttp
import time
async def get():
async with aiohttp.ClientSession() as s:
# The response is already closed after this `with` block.
# Why would it need to be used as a context manager?
resp = await s.request('GET', 'https://www.stackoverflow.com')
resp2 = await s.request('GET', 'https://www.github.com')
print("resp:",resp._closed)
print("resp:",resp._connection)
print("resp2:",resp2._closed)
print("resp2:",resp2._connection)
s.close()
print(s.closed)
c = await resp.text()
d = await resp2.text()
print()
print(s._connector)
print("resp:",resp._closed)
print("resp:",resp._connection)
print("resp2:",resp2._closed)
print("resp2:",resp2._connection)
loop = asyncio.get_event_loop()
loop.run_until_complete(get()) # Python 3.5 +
#dead loop
我的理解是,当脚本完成时,它们会自动关闭。但是通过什么手段呢@kcorlidy我想知道当会话调用\uuuuu exit\uuuu
或\uuuu aexit\uuu
时,响应本身是如何关闭的。在aiohttp中,当您使用..as退出时,上下文管理器将触发\uuu aexit\uu
<代码>\uuuu aexit\uuuu
包含self.close(),它可以关闭TCPConnector
。您可以查看aiohttp/client.py
第864 803 150 132行。请求
更复杂,我发现它使用的是PoolManager
。并使用manager.connection\u from_host('xx.com').urlopen(url=”等代码打开urlhttp://xx.com/,method=“get”)
。但我仍然不知道为什么连接完成后会关闭。顺便说一句,当使用…as退出时,close()
将迭代每个连接并关闭它(view session.py 733),它将在urlopen
完成后关闭。只需在注释和打印(x.closed)
上尝试我的代码。有趣的是,池管理器的第一个示例特别有用。在这一点上,我想知道两件事:您提到,“在使用
块执行此之后,响应已经关闭。为什么需要将其用作上下文管理器?”。但是可以直接从aiohttp客户机文档中看到这样一个示例:其次,在您调用其.text()
方法之前关闭resp
是毫无意义的(在会话调用\uu aexit\uu
之前,仍然在with
块中)。为什么resp
会关闭,而resp2
显示False
?为什么它需要用作上下文管理器?
在请求中,上下文管理器将关闭会话,实际上它清除了池管理器(我的第二部分代码显示)。在aiohttp
中,上下文管理器用于关闭TCPConnector,但s.request
仅从主机建立连接,请求仍在运行wait x.text()
只能在收到响应后执行。这就是为什么如果删除wait x.text()
,您将获得x.。\u connection->connectionresp.\u connection或resp2.\u connection
将保留,如果在获得响应之前退出。但实际上连接是关闭的@布拉德所罗门
# session.py
def __exit__(self, *args): #line 423
self.close()
def close(self): #line 733
for v in self.adapters.values():
v.close()
# adapters.py
# v.close()
def close(self): #line 307
self.poolmanager.clear()
for proxy in self.proxy_manager.values():
proxy.clear()
if connector is None: #line 132
connector = TCPConnector(loop=loop)
...
self._connector = connector #line 151
# connection timeout
try:
with CeilTimeout(real_timeout.connect,loop=self._loop):
assert self._connector is not None
conn = await self._connector.connect(
req,
traces=traces,
timeout=real_timeout
)
...
async def close(self) -> None:
if not self.closed:
if self._connector is not None and self._connector_owner:
self._connector.close()
self._connector = None
...
async def __aexit__(self,
...) -> None:
await self.close()
import asyncio
import aiohttp
import time
async def get():
async with aiohttp.ClientSession() as s:
# The response is already closed after this `with` block.
# Why would it need to be used as a context manager?
resp = await s.request('GET', 'https://www.stackoverflow.com')
resp2 = await s.request('GET', 'https://www.github.com')
print("resp:",resp._closed)
print("resp:",resp._connection)
print("resp2:",resp2._closed)
print("resp2:",resp2._connection)
s.close()
print(s.closed)
c = await resp.text()
d = await resp2.text()
print()
print(s._connector)
print("resp:",resp._closed)
print("resp:",resp._connection)
print("resp2:",resp2._closed)
print("resp2:",resp2._connection)
loop = asyncio.get_event_loop()
loop.run_until_complete(get()) # Python 3.5 +
#dead loop