简体   繁体   English

如何关闭aiohttp ClientSession

[英]How to close aiohttp ClientSession

I am trying to make an app that might live for a day, a week or longer. 我正在尝试制作可能会运行一天,一周或更长时间的应用程序。 Dyring the app's lifetime, it will make requests to different API's. 利用应用程序的生命周期,它将向不同的API发出请求。 Some of these apis might require log in, so it is important that i have access to cookies at all times. 其中一些API可能需要登录,因此重要的一点是,我始终可以访问Cookie。

So what i need is a file that the different API's can use without blocking the app. 因此,我需要的是一个可以在不阻止应用程序的情况下使用不同API的文件。

I am new to asynchronous programming(asyncio/aiohttp) and examples i have seen, shows how to make a lot of requests from a list of url's, but this is not what i need. 我是异步编程(asyncio / aiohttp)的新手,并且看到了一些示例,展示了如何从url列表中进行很多请求,但这不是我所需要的。

The problem with the code i have is, either i get ClientSession is closed error or unclosed ClientSession warnings. 我拥有的代码存在的问题是,我得到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()

I do get a response from the server, however it is followed by this error/warning 我确实从服务器收到响应,但是此错误/警告之后

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>

If i uncomment the await session.close() and remove the pass i get a response from the server in the first request, followed by RuntimeError: Session is closed in the second request. 如果我取消注释await session.close()并删除pass我在第一个请求中收到来自服务器的响应,然后出现RuntimeError: Session is closed在第二个请求中RuntimeError: Session is closedRuntimeError: Session is closed

Ahh, i think i got it now. 啊,我想我明白了。

The warnings i got Unclosed client session and Unclosed connector was aiohttp telling me "hey, you forgot to close the session". 我收到未关闭的Unclosed client session和未关闭的Unclosed connector的警告是aiohttp告诉我“嘿,您忘了关闭会话”。 And this is exactly what happened with this small example. 这正是这个小例子所发生的。 Both calls to get_url would actually get a response from the server, and then the app would end. 实际上,两次调用get_url都会从服务器获得响应,然后该应用程序将结束。 So the session would then be left in an unclosed state when the app ended, which is why the abover warnings were shown. 因此,当应用程序结束时,会话将处于未关闭状态,这就是显示上述警告的原因。

I was not supposed to close the session after each request, since there would be no way of making a new request at that point, atleast not to my knowledge. 我不应该在每次请求后关闭该会话,因为在那时我无法做出新的请求,这是我所不知道的。 And that is why i got RuntimeError: Session is closed when trying to make a new request, once it was already closed. 这就是为什么我得到RuntimeError: Session is closed尝试发出新请求时, RuntimeError: Session is closed ,因此该RuntimeError: Session is closed已关闭。

So once i figured this out, i then created a close function, and simply called this before the loop(app) ended. 因此,一旦找出来,便创建了一个close函数,并在loop(app)结束之前简单地调用了此函数。 Now i get no warnings/errors. 现在我没有警告/错误。 And cookies are now shared between all request made(i think) while the app is running. 现在,在应用程序运行时,在所有发出的请求(我认为)之间共享cookie。 Whether they be GET or POST, and that was exactly what i wanted. 无论它们是GET还是POST,这正是我想要的。

I hope that someone else new to aiohttp/asyncio will benefit from this, as it took me some time(to long) to understand. 我希望aiohttp / asyncio的其他新手可以从中受益,因为我花了一些时间(很长时间)来理解。 As i am still new to aiohttp/asyncio, i don't know if this is the correct way of doing it, but at least it seems to work. 由于我对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