一些函数和类可以帮助您处理aiohttp会话
aiohttp-asynctools的Python项目详细描述
自动将会话管理添加到类
一些函数和类可以帮助您处理aiohttp客户端会话。这是在这个discussion之后制作的。这适用于修饰协同路由和异步生成器方法。
安装
pip install aiohttp-asynctools
tl;dr
通过以下4个步骤,快速、干净地向类中添加aiohttp.ClientSession
对象:
- 导入异步工具
- 扩展AsyncTools.AbstractSessionContainer
- (可选)在
__init__
方法中剪切会话实例。 - 用
@asynctools.attach_session
修饰异步方法/生成器以附加session
参数。
下面是一个使用数学api(http://api.mathjs.org/v4)的简单示例的样子:
importasyncioimportasynctools# 1.classMathRequests(asynctools.AbstractSessionContainer):# 2.def__init__(self):super().__init__(raise_for_status=True)# 3. (optional)@asynctools.attach_session# 4.asyncdefget_text(self,url,params,session=None):asyncwithsession.get(url,params=params)asresponse:returnawaitresponse.text()asyncdefget_square(self,value):returnawaitself.get_text("http://api.mathjs.org/v4",params={'expr':'{}^2'.format(value)})
注意__init__
方法必须在这里,如果它是空的,只需使用pass
关键字作为其内容。
现在,您已经准备好实例化一个MathRequests
上下文管理器,并开始使用单个aiohttp
session
请求数学服务(会话对MathRequests
用户隐藏):
fromaiohttpimportwebroutes=web.RouteTableDef()@routes.get('/squares')asyncdefindex(request):tasks=[]data=request.queryvalues=data['values'].split(',')# Use the object as a context manager (async with <context_manager> as <name>)asyncwithMathRequests()asmaths:results=awaitasyncio.gather(*[maths.get_square(v)forvinvalues])returnweb.json_response({'values':values,'results':results})maths_app=web.Application()maths_app.add_routes(routes)
您也可以显式地启动/关闭会话:
# ...maths.start_session()results=awaitasyncio.gather(*[maths.get_square(v)forvinvalues])maths.close_session()# ...
现在,您可以启动服务器并测试服务(这里的gunicorn示例):
gunicorn math_requests:maths_app --bind localhost:8085 --worker-class aiohttp.GunicornWebWorker --reload
curl 'http://localhost:8085/squares?values=1,2,3,4,5,6,7,8,9,10'
哪个应该输出:
{"values":["1","2","3","4","5","6","7","8","9","10"],"results":["1","4","9","16","25","36","49","64","81","100"]}
详细信息和说明
什么?
目标是帮助aiohttp用户以高效/干净的方式构建包含sessions对象的类。
为什么?
如果要构建使用aiohttp client发出请求的类,则在某些时候必须处理会话。 quickstart guide for aiohttp client有一个关于会话的重要注释。
importaiohttpasyncwithaiohttp.ClientSession()assession:asyncwithsession.get('http://httpbin.org/get')asresp:print(resp.status)print(awaitresp.text())...
Note
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 other one for Facebook APIs. Anyway making a session for every request is a very bad idea.
A session contains a connection pool inside. Connection reusage and keep-alives (both are on by default) may speed up total performance.
我们的目标是将一个会话附加到一个给定的对象上,在这个模块中,您只需要4行(非常简单)代码就可以简单地实现这个目标。
怎么做?
<>模块提供了一个抽象类^ {< CD11>}和一个方法装饰器{{CD12}},您必须使用它来自动将会话管理添加到现有的类中。假设您有一个类MathRequests
,它有一个方法get_square
,使用位于http://api.mathjs.org/v4的math api服务的aiohttp.get
请求返回给定参数的平方值。以下是您的班级目前的情况:
importasyncio,aiohttproutes=aiohttp.web.RouteTableDef()classMathRequests:asyncdefget_text(self,url,params):# Remember "making a session for every request is a very bad idea"asyncwithaiohttp.ClientSession()assession:asyncwithsession.get(url,params=params)asresponse:returnawaitresponse.text()asyncdefget_square(self,value):returnawaitself.get_text("http://api.mathjs.org/v4",params={'expr':'{}^2'.format(value)})@routes.get('/squares')asyncdefindex(request):tasks=[]data=request.queryvalues=data['values'].split(',')maths=MathRequests()results=awaitasyncio.gather(*[maths.get_square(v)forvinvalues])returnweb.json_response({'values':values,'results':results})maths_app=aiohttp.web.Application()maths_app.add_routes(routes)
正如aiohttp
文档所说,这样实现MathRequests
是个坏主意,我们需要为所有get_square
请求共享一个会话。
一个简单的解决方案是在MathRequests
中存储一个客户端会话对象,您可以在__init__
方法中启动它。不幸的是,这不是一个非常干净的解决方案,因为aiohttp会话应该以同步的方式实例化(在偶数循环之外)。有关在coroutine之外创建会话的更多信息,请参见aiohttp#1468。
下面是使用提供的模块的最终解决方案asynctools
:
importasyncioimportasynctools# 1) Import# 2) Extends the abstract class that will handle the aiohttp session for you:classMathRequests(asynctools.AbstractSessionContainer):def__init__(self):# 2') (optional) initilise with any 'aiohttp.ClientSession' argumentsuper().__init__(raise_for_status=True)# 3) This decorator will automatically fill the session argument:@asynctools.attach_sessionasyncdefget_text(self,url,params,session=None):# 4) Add the 'session' argumentasyncwithsession.get(url,params=params)asresponse:returnawaitresponse.text()asyncdefget_square(self,value):returnawaitself.get_text("http://api.mathjs.org/v4",params={'expr':'{}^2'.format(value)})fromaiohttpimportwebroutes=web.RouteTableDef()@routes.get('/squares')asyncdefindex(request):tasks=[]data=request.queryvalues=data['values'].split(',')asyncwithMathRequests()asmaths:# Use the object as a context manager (async with <context_manager> as <name>)results=awaitasyncio.gather(*[maths.get_square(v)forvinvalues])returnweb.json_response({'values':values,'results':results})maths_app=web.Application()maths_app.add_routes(routes)
使用MathRequests
作为context manager是最好的选择(因为它将确保会话正确启动和关闭),但它不是唯一的选择,您还可以保持代码原来的状态:
@routes.get('/squares')asyncdefindex(request):tasks=[]data=request.queryvalues=data['values'].split(',')maths=MathRequests()results=awaitasyncio.gather(*[maths.get_square(v)forvinvalues])returnweb.json_response({'values':values,'results':results})
在这种情况下,没有会话附加到maths
对象,对get_square
的每个调用都将使用不同的会话(这与旧版本的MathRequests
一样糟糕)。要避免这种情况,可以做的是显式地打开一个“数学会话”,它将使所有的get_square
调用使用同一会话(同时,完成后不要忘记关闭会话):
@routes.get('/maths')asyncdefindex(request):tasks=[]data=request.queryvalues=data['values'].split(',')maths=MathRequests()maths.start_session()results=awaitasyncio.gather(*[maths.get_square(v)forvinvalues])maths.close_session()returnweb.json_response({'values':values,'results':results})