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

2024-10-02 10:27:25 发布

您现在位置:Python中文网/ 问答频道 /正文

我想使用http客户端作为类成员,但del函数无法调用waitclient.aclose()。 e、 g:

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 如何安全关闭


Tags: 函数importselfclienthttp客户端objectfoo
2条回答

虽然这是一个老问题,但我可能有一些令人信服的东西要分享,因为我有一个类似的情况。按照@Isabi的观点(回答2020-12-28),您需要使用事件循环将客户机与您的操作分离,然后手动控制其生命周期

在我的例子中,我需要对客户机进行更多的控制,这样我就可以将请求与发送分开,并在关闭客户机时利用会话池等。下面提供的示例演示了如何使用http.AsyncClient作为类成员,并在退出时关闭它

在解决这个问题的过程中,我遇到了一个异步学习曲线,但很快发现它是。。。其实不算太坏。它不像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,我最初编写了一些带有闭包的方法,因为它们似乎有意义;最后,通过将闭包转换为类方法,我能够(IMHO)在分离/封装/隔离之间提供良好的平衡

由此产生的使用界面感觉平易近人且易于阅读——我看到自己正在编写基于类的异步

问题可能是因为client.aclose()返回了一个awaitable,这在正常的def函数中是无法等待的

也许值得尝试一下asyncio.run(self.client.aclose())。这里可能会出现一个异常,抱怨您使用的是与当前运行的event loop不同的event loop(或者相同的,我不太了解您的上下文,所以我无法判断)。在这种情况下,您可以获取当前正在运行的event loop并从那里运行函数

请参阅https://docs.python.org/3/library/asyncio-eventloop.html了解有关如何完成此任务的更多信息

相关问题 更多 >

    热门问题