Requests/aiohttp: 关闭响应对象
Requests/aiohttp: closing response objects
我对在 requests
和 aiohttp
中都需要 .close()
响应对象感到有点困惑。 (请注意,这是一个单独的实例方法,而不是 session.close()
——我说的是响应对象本身。)
Response
(requests
) 或 ClientResponse
(aiohttp
) 是否需要显式调用 .close()
?
- 如果不是,使用响应本身作为上下文管理器的目的是什么? (
async with session.request('GET', 'https://www.pastebin.com'
) 下面。)如果它如下所示隐式关闭,为什么要为此定义两个 dunder 方法?
一些简单的测试(如下)似乎暗示当响应在会话上下文管理器中定义时会自动关闭。(它本身调用 self.close()
__exit__
或__aexit__
。但这是Session的关闭,不是Response对象。)
示例 - requests
>>> import requests
>>>
>>> with requests.Session() as s:
... resp = s.request('GET', 'https://www.pastebin.com')
... resp.raise_for_status()
... print(resp.raw.closed) # `raw` is urllib3.response.HTTPResponse object
... print(resp.raw._pool)
... print(resp.raw._connection)
... c = resp.text
...
True
HTTPSConnectionPool(host='pastebin.com', port=443)
None
>>>
>>> while 1:
... print(resp.raw.closed)
... print(resp.raw._pool)
... print(resp.raw._connection)
... break
...
True
HTTPSConnectionPool(host='pastebin.com', port=443)
None
示例 - aiohttp
>>> import asyncio
>>> import aiohttp
>>>
>>> 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.pastebin.com')
... print(resp._closed)
... print(resp._connection)
... print(resp._released)
... c = await resp.text()
... print()
... print(resp._closed)
... print(resp._connection)
... print(resp._released)
... return c
...
>>> c = asyncio.run(get()) # Python 3.7 +
False
Connection<ConnectionKey(host='pastebin.com', port=443, is_ssl=True, ssl=None, proxy=None, proxy_auth=None, proxy_headers_hash=None)>
False
True
None
False
这是 requests.models.Response
的来源。 "Should not normally need to be called explicitly" 是什么意思?有哪些例外情况?
def close(self):
"""Releases the connection back to the pool. Once this method has been
called the underlying ``raw`` object must not be accessed again.
*Note: Should not normally need to be called explicitly.*
"""
if not self._content_consumed:
self.raw.close()
release_conn = getattr(self.raw, 'release_conn', None)
if release_conn is not None:
release_conn()
Requests
:您无需显式调用 close()
。请求完成后会自动关闭,因为它是基于urlopen的(这就是为什么resp.raw.closed
是真的),这是我看了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
那__exit__
做了什么?它用于清除 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_POOLSIZE = 10
(http/https 是独立的)。这意味着如果你想在一个会话中访问超过 10 个网站,你可以选择关闭一些你不需要的网站,否则当你有一个会话时,管理器将关闭从第一个到最新的连接。但实际上你不需要关心这个,你可以指定池大小并且重建连接不会浪费太多时间
aiohttp
aiohttp.ClientSession() 正在为所有请求使用一个 TCPConnector。当它触发 __aexit__
时, self._connector
将被关闭。
编辑:s.request()
已建立来自主机的连接,但未收到响应。 await 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.whosebug.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
我对在 requests
和 aiohttp
中都需要 .close()
响应对象感到有点困惑。 (请注意,这是一个单独的实例方法,而不是 session.close()
——我说的是响应对象本身。)
Response
(requests
) 或ClientResponse
(aiohttp
) 是否需要显式调用.close()
?- 如果不是,使用响应本身作为上下文管理器的目的是什么? (
async with session.request('GET', 'https://www.pastebin.com'
) 下面。)如果它如下所示隐式关闭,为什么要为此定义两个 dunder 方法?
一些简单的测试(如下)似乎暗示当响应在会话上下文管理器中定义时会自动关闭。(它本身调用 self.close()
__exit__
或__aexit__
。但这是Session的关闭,不是Response对象。)
示例 - requests
>>> import requests
>>>
>>> with requests.Session() as s:
... resp = s.request('GET', 'https://www.pastebin.com')
... resp.raise_for_status()
... print(resp.raw.closed) # `raw` is urllib3.response.HTTPResponse object
... print(resp.raw._pool)
... print(resp.raw._connection)
... c = resp.text
...
True
HTTPSConnectionPool(host='pastebin.com', port=443)
None
>>>
>>> while 1:
... print(resp.raw.closed)
... print(resp.raw._pool)
... print(resp.raw._connection)
... break
...
True
HTTPSConnectionPool(host='pastebin.com', port=443)
None
示例 - aiohttp
>>> import asyncio
>>> import aiohttp
>>>
>>> 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.pastebin.com')
... print(resp._closed)
... print(resp._connection)
... print(resp._released)
... c = await resp.text()
... print()
... print(resp._closed)
... print(resp._connection)
... print(resp._released)
... return c
...
>>> c = asyncio.run(get()) # Python 3.7 +
False
Connection<ConnectionKey(host='pastebin.com', port=443, is_ssl=True, ssl=None, proxy=None, proxy_auth=None, proxy_headers_hash=None)>
False
True
None
False
这是 requests.models.Response
的来源。 "Should not normally need to be called explicitly" 是什么意思?有哪些例外情况?
def close(self):
"""Releases the connection back to the pool. Once this method has been
called the underlying ``raw`` object must not be accessed again.
*Note: Should not normally need to be called explicitly.*
"""
if not self._content_consumed:
self.raw.close()
release_conn = getattr(self.raw, 'release_conn', None)
if release_conn is not None:
release_conn()
Requests
:您无需显式调用 close()
。请求完成后会自动关闭,因为它是基于urlopen的(这就是为什么resp.raw.closed
是真的),这是我看了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
那__exit__
做了什么?它用于清除 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_POOLSIZE = 10
(http/https 是独立的)。这意味着如果你想在一个会话中访问超过 10 个网站,你可以选择关闭一些你不需要的网站,否则当你有一个会话时,管理器将关闭从第一个到最新的连接。但实际上你不需要关心这个,你可以指定池大小并且重建连接不会浪费太多时间
aiohttp
aiohttp.ClientSession() 正在为所有请求使用一个 TCPConnector。当它触发 __aexit__
时, self._connector
将被关闭。
编辑:s.request()
已建立来自主机的连接,但未收到响应。 await 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.whosebug.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