0 小序
Flask是基于Python实现的轻量级Web应用框架,WSGI工具箱采用Werkzeug,模板引擎使用Jinja2,具有BSD授权.
1 应用
1.0 结构
flask_pure.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def connect():
return "connected test"
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8088, debug=True)
1.2 运行
python flask_pure.py
或者
python3 flask_pure.py
* Serving Flask app "flask_pure" (lazy loading)
* Environment: production
WARNING: Do not use the development server in a production environment.
Use a production WSGI server instead.
* Debug mode: on
* Running on http://0.0.0.0:8088/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 732-366-147
1.3 客户端调用
运行服务后,有一行提示,* Running on http://0.0.0.0:8088/ (Press CTRL+C to quit)
即是该服务的运行地址,路由根据编写的逻辑进行修改及调用,终端运行是,使用Ctl + C
退出服务.
http://0.0.0.0:8088/
2 纯
Flask框架非阻塞特性
Flask实现的Web框架接口间是非阻塞的,即调用一个接口,不需要等待返回结果,可直接调用同一服务下的另一个接口,同一接口也是非阻塞的,即可实现并发.
2.1 验证
flask_pure.py
from flask import Flask, render_template
import time
app = Flask(__name__)
@app.route('/')
def connect():
return "connected test"
@app.route('/index')
def index_test():
print("xindaqi")
time.sleep(15)
return render_template('index.html')
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8088, debug=True)
2.2 启动
python flask_pure.py
或者
python3 flask_pure.py
* Serving Flask app "flask_pure" (lazy loading)
* Environment: production
WARNING: Do not use the development server in a production environment.
Use a production WSGI server instead.
* Debug mode: on
* Running on http://0.0.0.0:8088/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 732-366-147
2.3 调用接口
先调用index,延迟15s,即让客户端等待15s,在15s内调用第二个接口
http://localhost:8088/index
http://localhost:8088/
http://localhost:8088/index
http://localhost:8088/index
2.4 结果
xindaqi
127.0.0.1 - - [27/Jan/2019 10:20:05] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [27/Jan/2019 10:20:06] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [27/Jan/2019 10:20:06] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [27/Jan/2019 10:20:07] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [27/Jan/2019 10:20:07] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [27/Jan/2019 10:20:07] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [27/Jan/2019 10:20:07] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [27/Jan/2019 10:20:07] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [27/Jan/2019 10:20:16] "GET /index HTTP/1.1" 200 -
xindaqi
xindaqi
127.0.0.1 - - [27/Jan/2019 11:35:29] "GET /index HTTP/1.1" 200 -
127.0.0.1 - - [27/Jan/2019 11:35:32] "GET /index HTTP/1.1" 200 -
2.5 分析
- 由结果可见,调用第一个延迟15s的接口过程中,再调用其他接口,先返回能返回的结果;
- 没有出现阻塞现象,即其他接口可实时返回结果;
- 同时调用一个接口,不会出现
阻塞
,实现并发.
3 gevent
的阻塞和非阻塞特性
gevent
是一个基于协程的Python网络库,它使用greenlet在libev或libuv事件上提供高级同步API.
协程
是单线程下的并发,又称微线程,是一种用户态的轻量级线程,即协程由用户程序自己控制调度;
协程
切换开销小,属于程序级别的切换,操作系统无感知,因而更加轻量化,单线程内可实现并发效果,最大限度调用CPU.
协程
本质是单线程实现,无法利用多核,可以一个程序开启多个进程,每个进程开启多个线程,每个线程内开启协程;一旦协程出现阻塞,将会阻塞整个线程.
特点:
- 基于libev或libuv的快速事件循环
- 基于greenlets轻量执行单元
- 复用Python标准库
- 支持SSL与socket通信
- 通过线程池,dnspython或c等语言实现DNS查询
- 通过Monkey Patching实现调用第三方模块
- 支持TCP/UDPHTTP服务
- 支持进程及线程池
3.1 阻塞
3.1.0 Demo
flask_wsgi.py
from flask import Flask, render_template
from gevent.pywsgi import WSGIServer
import time
app = Flask(__name__)
@app.route('/')
def connect():
return "connected test"
@app.route('/index')
def index_test():
time.sleep(15)
return render_template('index.html')
if __name__ == "__main__":
server = WSGIServer(("0.0.0.0", 8089), app)
print("Server started")
server.serve_forever()
3.1.2 调用接口
先调用index,延迟15s,即让客户端等待15s,在15s内调用第二个接口
http://localhost:8088/index
http://localhost:8088/
3.1.3 结果
Server started
127.0.0.1 - - [2019-01-27 10:49:52] "GET /index HTTP/1.1" 200 240 15.028027
127.0.0.1 - - [2019-01-27 10:49:52] "GET / HTTP/1.1" 200 130 0.001366
3.1.4 分析
- 该程序未使用
monkey
- 由结果可见,调用第一个延迟15s的接口过程中,再调用其他接口,出现阻塞,直到第一个接口获取返回值后,第二个接口才有结果返回;
- 出现阻塞现象,即其他接口等待15s才能返回;
3.2 非阻塞
monkey
将标准socket模块中的函数与类替换为对用的功能项,这样即使不清楚gevent
结构,也可从多个greenlet
运行环境中受益.
3.2.1 Demo
flask_wsgi.py
from flask import Flask, render_template
from gevent.pywsgi import WSGIServer
from gevent import monkey
import time
monkey.patch_all()
app = Flask(__name__)
@app.route('/')
def connect():
return "connected test"
@app.route('/index')
def index_test():
time.sleep(15)
return render_template('index.html')
if __name__ == "__main__":
server = WSGIServer(("0.0.0.0", 8089), app)
print("Server started")
server.serve_forever()
3.2.2 调用接口
先调用index,延迟15s,即让客户端等待15s,在15s内调用第二个接口
http://localhost:8088/index
http://localhost:8088/
3.2.3 结果
Server started
127.0.0.1 - - [2019-01-27 11:09:28] "GET / HTTP/1.1" 200 130 0.000891
127.0.0.1 - - [2019-01-27 11:09:28] "GET / HTTP/1.1" 200 130 0.001959
127.0.0.1 - - [2019-01-27 11:09:37] "GET /index HTTP/1.1" 200 240 15.016904
3.2.4 分析
- 该程序使用
monkey
- 由结果可见,调用第一个延迟15s的接口过程中,再调用其他接口,先返回能返回的结果;
- 没有出现阻塞现象,即其他接口可实时返回结果;
4 总结
纯
Flask框架可实现接口间,及同一接口的非阻塞;genvent
用于实现socket通信,借助monkey可实现接口间及接口通信的非阻塞,即socket客户端可实时接入,多个客户端可非阻塞获取数据,若不使用monkey则会出现客户端调用同一接口时阻塞;- 流程简单,无注释;
[参考文献]
[1]http://flask.pocoo.org/
[2]http://www.gevent.org/