How to use an aiohttp ClientSession with Sanic?

梦想与她 提交于 2019-11-29 23:48:52

问题


I am trying to understand what is the right way to use aiohttp with Sanic.

From aiohttp documentation, I find the following:

Don’t create a session per request. Most likely you need a session per application which performs all requests altogether. More complex cases may require a session per site, e.g. one for Github and another one for Facebook APIs. Anyway making a session for every request is a very bad idea. A session contains a connection pool inside. Connection reuse and keep-alive (both are on by default) may speed up total performance.

And when I go to Sanic documentation I find an example like this:

This is an example:

from sanic import Sanic
from sanic.response import json

import asyncio
import aiohttp

app = Sanic(__name__)

sem = None

@app.route("/")
async def test(request):
    """
    Download and serve example JSON
    """
    url = "https://api.github.com/repos/channelcat/sanic"

    async with aiohttp.ClientSession() as session:
         async with sem, session.get(url) as response:
         return await response.json()

app.run(host="0.0.0.0", port=8000, workers=2)

Which is not the right way to manage an aiohttp session...

So what is the right way?
Should I init a session in the app and inject the session to all the methods in all layers?

The only issue I found is this but this doesn't help because I need to make my own classes to use the session, and not sanic.
Also found this in Sanic documentation, which says you shouldn't create a session outside of an eventloop.

I am a little confused :( What is the right way to go?


回答1:


In order to use a single aiohttp.ClientSession we need to instantiate the session only once and use that specific instance in the rest of the application.

To achieve this we can use a before_server_start listener which will allow us to create the instance before the app serves the first byte.

from sanic import Sanic 
from sanic.response import json

import aiohttp

app = Sanic(__name__)

@app.listener('before_server_start')
def init(app, loop):
    app.aiohttp_session = aiohttp.ClientSession(loop=loop)

@app.listener('after_server_stop')
def finish(app, loop):
    loop.run_until_complete(app.aiohttp_session.close())
    loop.close()

@app.route("/")
async def test(request):
    """
    Download and serve example JSON
    """
    url = "https://api.github.com/repos/channelcat/sanic"

    async with app.aiohttp_session.get(url) as response:
        return await response.json()


app.run(host="0.0.0.0", port=8000, workers=2)

Breakdown of the code:

  • We are creating an aiohttp.ClientSession, passing as argument the loop that Sanic apps create at the start, avoiding this pitfall in the process.
  • We store that session in the Sanic app.
  • Finally, we are using this session to make our requests.



回答2:


That is essentially what I am doing.

I created a module (interactions.py) that has, for example a function like this:

async def get(url, headers=None, **kwargs):
    async with aiohttp.ClientSession() as session:
        log.debug(f'Fetching {url}')
        async with session.get(url, headers=headers, ssl=ssl) as response:
            try:
                return await response.json()
            except Exception as e:
                log.error(f'Unable to complete interaction: {e}')
                return await response.text()

Then I just await on that:

results = await interactions.get(url)

I am not sure why that is not the "right way". The session (at least for my needs) can be closed as soon as my request is done.



来源:https://stackoverflow.com/questions/51638347/how-to-use-an-aiohttp-clientsession-with-sanic

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!