我正在尝试制作一个可能存活一天,一周或更长时间的应用程序。 在应用程序的生命周期内,它将向不同的API发出请求。其中一些 API 可能需要登录,因此重要的是 我可以随时访问饼干。
所以我需要一个不同 API 可以使用的文件而不会阻止应用程序。
我是异步编程(asyncio/aiohttp(的新手,我看到的示例显示了如何从url列表中发出大量请求,但这不是我需要的。
我拥有的代码的问题是,要么我收到客户端会话已关闭错误或未关闭的客户端会话警告。
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
。
啊,我想我现在明白了。
我得到的警告Unclosed client session
和Unclosed connector
是aiohttp告诉我"嘿,你忘了关闭会话"。 这正是这个小例子所发生的事情。对get_url
的两个调用实际上都会从服务器获得响应,然后应用程序将结束。 因此,当应用程序结束时,会话将处于未关闭状态,这就是显示 abover 警告的原因。
我不应该在每次请求后关闭会话,因为那时无法提出新请求,至少据我所知。 这就是为什么一旦它已经关闭,我在尝试提出新请求时会RuntimeError: Session is closed
。
因此,一旦我想通了这一点,我就创建了一个关闭函数,并在循环(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()