首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何关闭aiohttp ClientSession

如何关闭aiohttp ClientSession
EN

Stack Overflow用户
提问于 2018-09-08 03:25:23
回答 1查看 11.8K关注 0票数 8

我正在尝试做一个应用程序,可能会存活一天,一周或更长时间。在应用程序的生命周期内,它会向不同的API发出请求。其中一些API可能需要登录,因此我必须随时访问cookies。

所以我需要的是一个文件,不同的API可以使用而不会阻塞应用程序。

我对异步编程(asyncio/aiohttp)是个新手,我见过的例子展示了如何从一个url列表中发出很多请求,但这不是我需要的。

我的代码的问题是,要么得到ClientSession is closed错误,要么得到未关闭的ClientSession警告。

代码语言:javascript
复制
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()

我确实收到了来自服务器的响应,但是它后面跟着这个错误/警告

代码语言:javascript
复制
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

EN

回答 1

Stack Overflow用户

发布于 2018-09-13 23:44:38

啊,我想我现在明白了。

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

我不应该在每次请求后关闭会话,因为那时没有办法发出新的请求,至少据我所知不是这样。这就是为什么我在尝试发出新请求时得到了RuntimeError: Session is closed,因为它已经关闭了。

因此,一旦我弄清楚了这一点,我就创建了一个close函数,并在循环(App)结束之前简单地调用它。现在,我没有收到任何警告/错误。现在,在应用程序运行时,所有请求(我认为)都会共享cookie。无论是GET还是POST,这正是我想要的。

我希望其他刚接触aiohttp/asyncio的人能从中受益,因为我花了很长时间才明白这一点。由于我对aiohttp/asyncio还不熟悉,我不知道这是不是正确的方式,但至少它看起来是有效的。

代码语言:javascript
复制
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()
票数 9
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52228550

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档