繁体   English   中英

如何关闭aiohttp ClientSession

[英]How to close aiohttp ClientSession

我正在尝试制作可能会运行一天,一周或更长时间的应用程序。 利用应用程序的生命周期,它将向不同的API发出请求。 其中一些API可能需要登录,因此重要的一点是,我始终可以访问Cookie。

因此,我需要的是一个可以在不阻止应用程序的情况下使用不同API的文件。

我是异步编程(asyncio / aiohttp)的新手,并且看到了一些示例,展示了如何从url列表中进行很多请求,但这不是我所需要的。

我拥有的代码存在的问题是,我得到ClientSession关闭错误或未关闭ClientSession警告。

import asyncio  # only here for debugging purposes
import aiohttp

USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:61.0) Gecko/20100101 Firefox/61.1'


def default_headers():
    header = {
        'User-Agent': USER_AGENT
    }
    return header


class WebSession(object):
    session = None

    @classmethod
    def create(cls):
        cls.session = aiohttp.ClientSession()
        return cls.session

    @classmethod
    def close(cls):
        if cls.session is not None:
            cls.session.close()

async def request(method, url, **kwargs):

    if kwargs.get('headers', None) is None:
        kwargs['headers'] = default_headers()

    if WebSession.session is None:
        session = WebSession.create()
    else:
        session = WebSession.session


    async with session.request(method=method, url=url, **kwargs) as response:
        if isinstance(session, aiohttp.ClientSession):
            # if i close the session here, i will get the ClientSession closed error on 2. request.
            # await session.close()
            pass

        return response


async def get(url, **kwargs):
    return await request('GET', url=url, **kwargs)


async def post(url, **kwargs):
    return await request('POST', url=url, **kwargs)


async def get_url():
    res = await get('https://httpbin.org/get')
    print(f'Status code: {res.headers}')


m_loop = asyncio.get_event_loop()
m_loop.run_until_complete(get_url())
# if i run this without closing the ClientSession, i will get unclosed ClientSession warnings.
m_loop.run_until_complete(get_url())
m_loop.close()

我确实从服务器收到响应,但是此错误/警告之后

Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x03354630>
Unclosed connector
connections: ['[(<aiohttp.client_proto.ResponseHandler object at   0x033BBBF0>, 71.542)]']
connector: <aiohttp.connector.TCPConnector object at 0x033542D0>

如果我取消注释await session.close()并删除pass我在第一个请求中收到来自服务器的响应,然后出现RuntimeError: Session is closed在第二个请求中RuntimeError: Session is closedRuntimeError: Session is closed

啊,我想我明白了。

我收到未关闭的Unclosed client session和未关闭的Unclosed connector的警告是aiohttp告诉我“嘿,您忘了关闭会话”。 这正是这个小例子所发生的。 实际上,两次调用get_url都会从服务器获得响应,然后该应用程序将结束。 因此,当应用程序结束时,会话将处于未关闭状态,这就是显示上述警告的原因。

我不应该在每次请求后关闭该会话,因为在那时我无法做出新的请求,这是我所不知道的。 这就是为什么我得到RuntimeError: Session is closed尝试发出新请求时, RuntimeError: Session is closed ,因此该RuntimeError: Session is closed已关闭。

因此,一旦找出来,便创建了一个close函数,并在loop(app)结束之前简单地调用了此函数。 现在我没有警告/错误。 现在,在应用程序运行时,在所有发出的请求(我认为)之间共享cookie。 无论它们是GET还是POST,这正是我想要的。

我希望aiohttp / asyncio的其他新手可以从中受益,因为我花了一些时间(很长时间)来理解。 由于我对aiohttp / asyncio还是陌生的,所以我不知道这是否是正确的方法,但至少它似乎有效。

import asyncio  # only here for debugging purposes
import aiohttp

USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:61.0) Gecko/20100101 Firefox/61.1'


def default_headers():
    header = {
        'User-Agent': USER_AGENT
    }
    return header


class WebSession(object):
    session = None

    @classmethod
    def create(cls):
        cls.session = aiohttp.ClientSession()
        return cls.session

    @classmethod
    def close(cls):
        if cls.session is not None:
            # apparently this is supposed to return a future?
            return cls.session.close()


async def request(method, url, **kwargs):

    if kwargs.get('headers', None) is None:
        kwargs['headers'] = default_headers()

    if WebSession.session is None:
        session = WebSession.create()
    else:
        session = WebSession.session

    return await session.request(method=method, url=url, **kwargs)


async def get(url, **kwargs):
    return await request('GET', url=url, **kwargs)


async def post(url, **kwargs):
    return await request('POST', url=url, **kwargs)


async def get_url():
    res = await get('https://httpbin.org/get')
    print(f'Headers: {res.headers}')


async def close():
    # run this before the app ends
    await WebSession.close()

# so imagine that this is our app.
m_loop = asyncio.get_event_loop()
# its running now and doing stuff..

# then it makes a request to a url.
m_loop.run_until_complete(get_url())
# then some time passes, and then it makes another request to a url.
m_loop.run_until_complete(get_url())
# now the app gets stopped, whether by keyboard interrupt or some other means of stopping it
# then close the session
m_loop.run_until_complete(close())
# and then end the app..
m_loop.close()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM