如何关闭 aiohttp ClientSession [英] How to close aiohttp ClientSession

查看:96
本文介绍了如何关闭 aiohttp ClientSession的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试制作一个可以存活一天、一周或更长时间的应用.在应用程序的生命周期中,它会向不同的 API 发出请求.其中一些 api 可能需要登录,因此重要的是我可以随时访问 cookie.

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. Some of these apis might require log in, so it is important that i have access to cookies at all times.

所以我需要的是一个不同的 API 可以在不阻塞应用程序的情况下使用的文件.

So what i need is a file that the different API's can use without blocking the app.

我是异步编程(asyncio/aiohttp)的新手,我见过的示例展示了如何从 url 列表发出大量请求,但这不是我需要的.

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.

我的代码的问题是,我收到 ClientSession 已关闭错误或未关闭 ClientSession 警告.

The problem with the code i have is, either i get ClientSession is closed error or unclosed ClientSession warnings.

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>

如果我取消对 await session.close() 的注释并删除 pass,我会在第一个请求中收到来自服务器的响应,然后是 RuntimeError:会话在第二个请求中关闭.

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.

推荐答案

啊,我想我现在明白了.

Ahh, i think i got it now.

我收到未关闭的客户端会话未关闭的连接器的警告是aiohttp告诉我嘿,你忘记关闭会话了".这正是这个小例子发生的事情.对 get_url 的两次调用实际上都会从服务器获得响应,然后应用程序将结束.因此,当应用程序结束时,会话将处于未关闭状态,这就是显示上述警告的原因.

The warnings i got Unclosed client session and Unclosed connector was aiohttp telling me "hey, you forgot to close the session". 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. So the session would then be left in an unclosed state when the app ended, which is why the abover warnings were shown.

我不应该在每次请求后关闭会话,因为那时无法发出新请求,至少我不知道.这就是为什么我在尝试发出新请求时收到 RuntimeError: Session is closed,一旦它已经关闭.

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.

所以一旦我想通了这一点,我就创建了一个关闭函数,并在循环(应用程序)结束之前简单地调用了它.现在我没有收到警告/错误.现在,在应用程序运行时,所有请求(我认为)之间都会共享 cookie.无论它们是 GET 还是 POST,这正是我想要的.

So once i figured this out, i then created a close function, and simply called this before the loop(app) ended. Now i get no warnings/errors. And cookies are now shared between all request made(i think) while the app is running. Whether they be GET or POST, and that was exactly what i wanted.

我希望其他不熟悉 aiohttp/asyncio 的人会从中受益,因为我花了一些时间(太长)来理解.由于我还是 aiohttp/asyncio 的新手,我不知道这是否是正确的做法,但至少它似乎有效.

I hope that someone else new to aiohttp/asyncio will benefit from this, as it took me some time(to long) to understand. 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.

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()

这篇关于如何关闭 aiohttp ClientSession的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆