如何重用aiohttp ClientSession池?

dvt*_*tan 13 python-asyncio aiohttp sanic

文档说要重用ClientSession:

不要为每个请求创建会话.很可能每个应用程序都需要一个会话来完成所有请求.

会话内部包含连接池,连接重用和保持活动(两者都默认打开)可以加快总体性能.1

但是在文档中似乎没有任何解释如何做到这一点?有一个例子可能是相关的,但它没有说明如何在其他地方重用池:http://aiohttp.readthedocs.io/en/stable/client.html#keep-alive-connection-pooling-and-cookie-分享

这样的事情是正确的做法吗?

@app.listener('before_server_start')
async def before_server_start(app, loop):
    app.pg_pool = await asyncpg.create_pool(**DB_CONFIG, loop=loop, max_size=100)
    app.http_session_pool = aiohttp.ClientSession()


@app.listener('after_server_stop')
async def after_server_stop(app, loop):
    app.http_session_pool.close()
    app.pg_pool.close()


@app.post("/api/register")
async def register(request):
    # json validation
    async with app.pg_pool.acquire() as pg:
        await pg.execute()  # create unactivated user in db
        async with app.http_session_pool as session:
            # TODO send activation email using SES API
            async with session.post('http://httpbin.org/post', data=b'data') as resp:
                print(resp.status)
                print(await resp.text())
        return HTTPResponse(status=204)
Run Code Online (Sandbox Code Playgroud)

Mik*_*mov 6

我认为可以改进的东西很少:

1)

实例ClientSession是一个会话对象.这个会话包含连接池,但它本身不是"session_pool".我建议重命名http_session_poolhttp_session或可能client_session.

2)

Session的close()方法是一个方法.你应该等待它:

await app.client_session.close()
Run Code Online (Sandbox Code Playgroud)

甚至更好(恕我直言),而不是考虑如何正确打开/关闭会话使用标准异步上下文管理器等待__aenter__/ __aexit__:

@app.listener('before_server_start')
async def before_server_start(app, loop):
    # ...
    app.client_session = await aiohttp.ClientSession().__aenter__()


@app.listener('after_server_stop')
async def after_server_stop(app, loop):
    await app.client_session.__aexit__(None, None, None)
    # ...
Run Code Online (Sandbox Code Playgroud)

3)

注意这个信息:

但是,如果在关闭基础连接之前停止事件循环,则会ResourceWarning: unclosed transport发出警告(启用警告时).

要避免这种情况,必须在关闭事件循环之前添加一个小延迟,以允许任何打开的底层连接关闭.

我不确定在你的情况下这是强制性的,但在await asyncio.sleep(0)内部添加after_server_stop文档建议并没有什么不好:

@app.listener('after_server_stop')
async def after_server_stop(app, loop):
    # ...
    await asyncio.sleep(0)  # http://aiohttp.readthedocs.io/en/stable/client.html#graceful-shutdown
Run Code Online (Sandbox Code Playgroud)

UPD:

实现__aenter__/ __aexit__可以用作异步上下文管理器的类(可以在async with语句中使用).它允许在执行内部块之前和之后执行一些操作.这与常规上下文管理器非常相似,但asyncio相关.与常规上下文管理器相同,异步可以直接使用(不async with)手动等待__aenter__/ __aexit__.

为什么我认为最好使用__aenter__/ __aexit__手动创建/释放会话而不是使用close(),例如?因为我们不应该担心__aenter__/ 里面实际发生了什么__aexit__.想象一下,在未来aiohttp的会话创建版本中将会更改,open()例如需要等待.如果您将使用__aenter__/ __aexit__您不需要以某种方式更改您的代码.