如何在 Sanic 中使用 aiohttp ClientSession?

How to use an aiohttp ClientSession with Sanic?

我想了解在 Sanic 中使用 aiohttp 的正确方法是什么。

从 aiohttp documentation,我发现以下内容:

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.

当我查看 Sanic 文档时,我发现了一个这样的例子:

这是一个例子:

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)

这不是管理 aiohttp 会话的正确方法...

那么正确的做法是什么?
我应该在应用程序中启动会话并将会话注入所有层中的所有方法吗?

我发现的唯一问题是 this 但这无济于事,因为我需要自己制作 类 才能使用会话,而不是 sanic。
还在 Sanic 文档中找到 this,它说你不应该在事件循环之外创建会话。

我有点困惑:( 正确的方法是什么?

这基本上就是我正在做的事情。

我创建了一个模块 (interactions.py),它具有例如这样的功能:

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

那我就await

results = await interactions.get(url)

我不确定为什么不是 "right way"。会话(至少满足我的需要)可以在我的请求完成后立即关闭。

为了使用单个 aiohttp.ClientSession 我们只需要实例化会话一次并在应用程序的其余部分使用该特定实例。

为了实现这一点,我们可以使用 before_server_start listener 这将允许我们在应用程序提供第一个字节之前创建实例。

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)

代码分解:

  • 我们正在创建一个 aiohttp.ClientSession,将 Sanic 应用程序在开始时创建的循环作为参数传递,避免在此过程中 this pitfall
  • 我们将该会话存储在 Sanic app
  • 最后,我们将使用此会话提出请求。