如何使用 httpx.AsyncClient 作为类成员,并异步关闭

问题描述

我想使用 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,所以我最初编写了一些带有闭包的方法,因为它们似乎有意义;最后,我能够(恕我直言)通过将闭包转换为类方法在分离/封装/隔离之间提供很好的平衡。

最终的使用界面让人感觉平易近人且易于阅读 - 我认为自己正在编写基于类的异步向前发展。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...