问题描述
我想使用 http 客户端作为类成员,但 del 函数无法调用 await client.aclose()
。
例如:
import httpx
class Foo(object):
def __init__(self):
self.client = httpx.AsyncClient()
def __del__(self):
await self.client.aclose()
参考:https://www.python-httpx.org/async/#opening-and-closing-clients 如何安全关闭?
解决方法
问题可能是由于 client.aclose()
返回了一个 awaitable
,而无法在普通的 def
函数中等待。
asyncio.run(self.client.aclose())
值得一试。在这里可能会发生异常,抱怨您使用的是与当前运行的 event loop
不同的 event loop
(或相同,我不太了解您的上下文,所以我无法判断)。在这种情况下,您可以获取当前运行的 public IActionResult Overview()
{
return View();
}
并从那里运行该函数。
有关如何完成它的更多信息,请参阅 https://docs.python.org/3/library/asyncio-eventloop.html。
,虽然这是一个较旧的问题,但由于我遇到了类似的情况,我可能有一些令人信服的内容要分享。对于@Isabi 的观点(2020 年 12 月 28 日已回答),您需要使用事件循环将客户端与您的操作分离,然后手动控制它的生命周期。
在我的情况下,我需要对客户端进行更多控制,以便我可以将请求与发送分开,并且当客户端关闭时,我可以利用会话池等。下面提供的示例显示了如何使用 http .AsyncClient 作为类成员并在退出时关闭它。
在弄清楚这一点时,我遇到了 Asyncio 学习曲线,但很快发现它......实际上还不错。它不像 Go[lang] 那样干净,但在摆弄一两个小时后它开始变得有意义了。完全披露:我仍然怀疑这是否 100% 正确。
关键部分在 __init__
、close
和 __del__
方法中。对我来说,还有待回答的是,在上下文管理器中使用 http.AsyncClient 是否实际上会重置连接等。我只能假设它确实如此,因为这对我来说很有意义。我不禁想知道:这有必要吗?
import asyncio
import httpx
import time
from typing import Callable,List
from rich import print
class DadJokes:
headers = dict(Accept='application/json')
def __init__(self):
"""
Since we want to reuse the client,we can't use a context manager that closes it.
We need to use a loop to exert more control over when the client is closed.
"""
self.client = httpx.AsyncClient(headers=self.headers)
self.loop = asyncio.get_event_loop()
async def close(self):
# httpx.AsyncClient.aclose must be awaited!
await self.client.aclose()
def __del__(self):
"""
A destructor is provided to ensure that the client and the event loop are closed at exit.
"""
# Use the loop to call async close,then stop/close loop.
self.loop.run_until_complete(self.close())
self.loop.close()
async def _get(self,url: str,idx: int = None):
start = time.time()
response = await self.client.get(url)
print(response.json(),int((time.time() - start) * 1000),idx)
def get(self,url: str):
self.loop.run_until_complete(self._get(url))
def get_many(self,urls: List[str]):
start = time.time()
group = asyncio.gather(*(self._get(url,idx=idx) for idx,url in enumerate(urls)))
self.loop.run_until_complete(group)
print("Runtime: ",int((time.time() - start) * 1000))
url = 'https://www.icanhazdadjoke.com'
dj = DadJokes()
dj.get_many([url for x in range(4)])
由于我最近一直在使用 Go,所以我最初编写了一些带有闭包的方法,因为它们似乎有意义;最后,我能够(恕我直言)通过将闭包转换为类方法在分离/封装/隔离之间提供很好的平衡。
最终的使用界面让人感觉平易近人且易于阅读 - 我认为自己正在编写基于类的异步向前发展。