Flask 的替代品 aiohttp 和 Quart
本来只是为了研究一下 Flask 怎么去支持早已在 Python 的支持的 coroutine 功能,没想步子越迈越大,直顶到 aiohttp Web 服务器和 Flask 的异步实现版本 Quart。Flask 得费了好一番功夫去获得
下面来稍稍体验一下用分别用 aiohttp 和 Quart 实现简单的异步服务器,我们的关注点在它的异步路由。
用
下面是一个简单的例子
由于 hello() 方法加了
不过
用
下面来看熟悉的味道,同样测试
Quart 同时支持异步和非异步的方法,这给了我们更多的灵活性,比如使用非
值得一试
一个获得
对于 Quart 中异步或同步路由中都想获得
线程上存在直接返回,否则创建一个
[版权声明]
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
EventLoop,可知 aiohttp 和 Quart 的路由方法直接就允许 async 的,那个 EventLoop 自然就在其中。从 async 的路由方法出发去调用别的异步方法就是一件十分轻松的事情。下面来稍稍体验一下用分别用 aiohttp 和 Quart 实现简单的异步服务器,我们的关注点在它的异步路由。
异步的 aiohttp Web 服务器
aiohttp 除了 HTTP 客户端功能,还有服务端端,因它的异步特性,可以用它建立一个异步的 Web 服务器,也就是它的路由方法也是异步的,完全可用它来替代 Flask 本身。用
pip install aiohttp 安装下面是一个简单的例子
1from aiohttp import web
2import asyncio
3import threading
4
5async def hello(request):
6 loop = asyncio.get_event_loop()
7 thread_name = threading.current_thread().name
8 return web.json_response({'event_loop': str(loop), 'thread': thread_name})
9
10app = web.Application()
11app.add_routes([web.get('/', hello)]) # 只能注册 async 修饰的方法
12web.run_app(app)由于 hello() 方法加了
async 关键字,可直接由路由 / 关联来执行,所以它是有一个 EventLoop 在里头的,加了一行代码在控制台打印出该 EventLoop。同是我们也来观察一下它用什么线程来处理客户端请求。aiohttp 服务启动的默认端口号是 8080======== Running on http://0.0.0.0:8080 ========发出请求
(Press CTRL+C to quit)
$ curl http://localhost:8080/比起 Flask 应用 coroutine 时不需要显式的用
{"event_loop": "<_UnixSelectorEventLoop running=True closed=False debug=False>", "thread": "MainThread"}
asyncio.run() 或用下面几行代码1loop = asyncio.new_event_loop()
2asyncio.set_event_loop(loop)
3
4loop.run_until_complete(asyncio.gather(tasks))不过
aiohttp 的服务总是用主线程去处理客户端请求,那就是说使用 aiohttp 做服务器的话,需要各个层所有的方法都是 async 的,这样多个请求之前才不至于互相阻塞。同时 aiohttp 用 app.add_routes() 注册路由时,只能支持 async 修饰的方法,也就是说 asiohttp 不支持非异步的方法。这要求后端的所有实现方法必须小心,一旦调用了非异步方法将阻塞其他的请求。Flask 的超集版本 Quart
Quart 直接被定义为 Flask 的超集,支持异步路由,使用了 Flask 的 API,支持 Flask 的扩展,还添加了一些 Flask 不具备的功能。Quart 当前版本 0.13.0,它从 0.7.0 开始需要 Python 3.7.0 或更高版本的。用
pip install quart 安装下面来看熟悉的味道,同样测试
async 和 非 async 两个路由方法 1from quart import Quart, jsonify
2import asyncio
3import threading
4
5app = Quart(__name__)
6
7@app.route('/async')
8async def hello_async():
9 loop = asyncio.get_event_loop()
10 thread_name = threading.current_thread().name
11 return jsonify({'event_loop': str(loop), 'thread': thread_name})
12
13@app.route('/sync')
14def hello_sync():
15 loop = None
16 try:
17 loop = asyncio.get_event_loop()
18 except RuntimeError:
19 pass
20 thread_name = threading.current_thread().name
21 return jsonify({'event_loop': str(loop), 'thread': thread_name})
22
23
24app.run(debug=True)python app.py 启动它,同样是监听在 5000 号端口Running on http://127.0.0.1:5000 (CTRL + C to quit)访问及结果
[2020-07-13 11:26:01,137] Running on 127.0.0.1:5000 over http (CTRL + C to quit)
$ curl http://localhost:5000/async同样,既然是路由方法上可用
{
"event_loop": "<_UnixSelectorEventLoop running=True closed=False debug=True>",
"thread": "MainThread"
} $ curl http://localhost:5000/sync
{
"event_loop": "None",
"thread": "ThreadPoolExecutor-0_0"
}
async 关键字,自然它在执行时能拿到当前的 EventLoop,调用其他的 async 方法不在话下。与 aiohttp 一样,async 的路由方法总是由主线程来处理请求。非 async 的路由方法由线程池来处理,这比 Flask 每请求创建一个新的线程要先进一些。Quart 同时支持异步和非异步的方法,这给了我们更多的灵活性,比如使用非
async 路由方法时,某些地方我们可以手动的用 EventLoop 来调度,而不一定要求一切 async。轻松搞上 websocket
Flask 支持 websocket 需要安装一个 flask-socketio 扩展,而 Quart 更简单,有装饰器支持1from quart import websocket
2
3@app.websocket('/ws')
4async def ws():
5 while True:
6 await websocket.sned('hello')值得一试
一个获得 EventLoop 的工具方法
对于 Quart 中异步或同步路由中都想获得 EventLoop 进行更精细的方法调度,可以用下面的工具方法来获得或创建一个新的 EventLoop 1def get_event_loop():
2 try:
3 event_loop = get_event_loop()
4 except RuntimeError:
5 event_loop = asyncio.new_event_loop()
6 asyncio.set_event_loop(event_loop)
7
8 if event_loop.is_running():
9 raise RuntimeError("called from a running event loop")
10 return event_loop线程上存在直接返回,否则创建一个
EventLoop, 如果是同步路由方法,必须自己用 event_loop.run_until_complete(...) 发起协程的执行,相当于进行 Promise 的最终兑现。总结一下:
- aiohttp 只支持
async路由方法,所有请求都在主线程中处理,任何非异步方法的调用都将阻塞其他的请求 - Quart 同时支持
async和非async路由方法,async路由由主线程处理,这一点与aiohttp的路由是一样的。 - Quart 的非
async路由方法由线程池处理,比 Flask 每次请求新建线程要好 - Quart 允许我们同时用非
async路由与EventLoop来控制 - 在 aiohttp 或 Quart 中使用
async路由时反而要倍加小心,最好是所有方法都是async的
[版权声明]
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。