2017-10-28 160 views
1

文檔說重複使用ClientSession:如何重用aiohttp ClientSession池?

不要爲每個請求創建會話。最有可能你需要一個會話,每個 應用程序完全執行所有請求。

會話中包含連接池,連接重用和keepalive(默認情況下都處於打開狀態)可能會加速整體性能。 1

但是在文檔中似乎沒有關於如何做到這一點的任何解釋?有一個例子可能是相關的,但它並沒有說明如何在其他地方重新使用該池:http://aiohttp.readthedocs.io/en/stable/client.html#keep-alive-connection-pooling-and-cookie-sharing

會是這樣的正確方法嗎?

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

回答

1

是有幾件事情,我認爲可以改進:

1)

的實例ClientSession是一個會話對象。這個會話包含連接池,但它本身不是「session_pool」。我建議將http_session_pool重命名爲http_session或可能client_session

2)

會話的close()方法is a corountine。你應該等待它:

await app.client_session.close() 

甚至更​​好(恕我直言),而不是想着如何正確地打開/關閉會話使用標準異步上下文管理與__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) 
    # ... 

3)

講究this info

但是,如果事件循環在底層連接 已關閉之前停止,則會發出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 

UPD:

類實現__aenter__/__aexit__可用作async context manager(可用於async with聲明)。它允許在執行內部塊之前和之後執行一些操作。這與常規上下文管理器非常相似,但asyncio有關。同樣作爲常規上下文管理器的異步一個可直接使用(無async with)手動等待__aenter__/__aexit__

爲什麼我認爲這是最好使用手動__aenter__/__aexit__,而不是使用close(),例如要創建/自由會話?因爲我們不應該擔心__aenter__/__aexit__內實際發生了什麼。想象一下,在未來版本aiohttp中,會話的創建將隨着需要等待open()而改變。如果您使用__aenter__/__aexit__您不需要以某種方式更改您的代碼。

+0

只要添加到3),它說如果使用SSL(即https?)則使用0.25s。另外,我討厭問,但是我還沒有在其他地方找到對'__aenter__' /'__aexit__'的任何解釋......請你解釋它有什麼不同? (看起來他們只是用'@ asyncio.coroutine'裝飾器包裝?) – dtgq

+1

@dtgq我更新了答案,添加了一些關於'__aenter__' /'__aexit__'的信息。什麼來到0.25s - 是的,如果你認爲你可能要求https網址,這是有道理的。 –