Sanic: aiohttp.ClientSession must be create in each request?

Created on 14 Mar 2017  路  9Comments  路  Source: sanic-org/sanic

https://github.com/channelcat/sanic/blob/master/examples/aiohttp_example.py#L23
Why not to save as a global variable, and using it in next request?

Most helpful comment

@qwIvan

I dont know if this is the best way but i solve this by adding the http/db access to the app.

# inside my utils.py

import aiohttp
import async_timeout

async def fetch(session, url, data=None, method='GET', r_type='json'):

    async with session.request(method, url, data=data) as response:
        if r_type == 'json':
            response = await response.json()
        elif r_type == 'text':
            response = await response.text()

        return response


async def http(url, *args, timeout=15, data=None, headers=None, method='GET', r_type='json'):
        uri = url

        if headers is None:
            headers = {}

        with async_timeout.timeout(timeout):
            async with aiohttp.ClientSession(headers=headers) as session:
                response = await fetch(session, uri, data=data, method=method)
                return response

So inside the handles i can just do:
````
from utils import http

@app.listener('before_server_start')
async def setup_something(app, loop):
app.http = http

@app.route('/')
async def handle(request):
http = await request.app.http(someurl)
````
I dont know if this is the best way, but its very nice way for db access if your using blueprints

All 9 comments

Here's my idea.
I think session is different between one in request and one in other request.And Sanic don't have mechanism like Flask to make a context ,to make sure that same code in different request will show different result.
So,It shouldn't be a global variable,I guess

@qwIvan although I said this was fine in chat I'm not sure. Since it uses a context manager you'll be skipping whatever portion happens on exit each time. I would definitely test this and see how it works. We did see performance improvements in the asyncpg example by maintaining a global connection pool.

@r0fls I use a global connection pool just like this

~~~Python
app = Sanic()
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())

async def get_pool(args, kwargs):
args[0].pool = {
"aiomysql": await aiomysql.create_pool(host="127.0.0.1", user="root", password="
", db="test_sanic",
maxsize=5),
"mysql": connect(host="127.0.0.1", user="root", password="
*", db="test_sanic", charset='utf8mb4',
cursorclass=cursors.DictCursor)}

@app.route("async_count")
async def test(request):
async with app.pool['aiomysql'].acquire() as conn:
async with conn.cursor() as cur:
await cur.execute("select count(*) from wocao ")
res = await cur.fetchall()
if res or len(res) > 0:
data = {}
data['data'] = res
return json(data)

@app.route("/normal_count")
async def test1(request):
connection = app.pool["mysql"]
with connection.cursor() as cursor:
cursor.execute("set autocommit = 1")
connection.commit()
cursor.execute("select count(*) from wocao")
res = cursor.fetchall()
if res or len(res) > 0:
data = {}
data['data'] = res
return json(data)

if __name__ == '__main__':
app.run(host="127.0.0.1", workers=4, port=12000, before_start=get_pool)

~~~

Could I make a PR for a new example ?

@Zheaoli yes, please do, that would be great. We don't have an aiomysql example yet.

I'll probably have feedback for you on the PR though, just FYI. Haven't fully read this script over yet.

@qwIvan

I dont know if this is the best way but i solve this by adding the http/db access to the app.

# inside my utils.py

import aiohttp
import async_timeout

async def fetch(session, url, data=None, method='GET', r_type='json'):

    async with session.request(method, url, data=data) as response:
        if r_type == 'json':
            response = await response.json()
        elif r_type == 'text':
            response = await response.text()

        return response


async def http(url, *args, timeout=15, data=None, headers=None, method='GET', r_type='json'):
        uri = url

        if headers is None:
            headers = {}

        with async_timeout.timeout(timeout):
            async with aiohttp.ClientSession(headers=headers) as session:
                response = await fetch(session, uri, data=data, method=method)
                return response

So inside the handles i can just do:
````
from utils import http

@app.listener('before_server_start')
async def setup_something(app, loop):
app.http = http

@app.route('/')
async def handle(request):
http = await request.app.http(someurl)
````
I dont know if this is the best way, but its very nice way for db access if your using blueprints

I'd suggest going with @Hellowlol's approach.

We need some sort of documentation to support this style of doing things...

@seemethere @Hellowlol
Isn't @Hellowlol's approach still creating a session in each request?

This is how I'm doing it:

@app.listener('before_server_start')
async def aiohttp_setup(app, loop):
    session = aiohttp.ClientSession(loop=loop)  # or your func to setup the session
    app.http = session
Was this page helpful?
0 / 5 - 0 ratings