借助于app.run(),就可以让Flask程序启动起来,并且提供Web服务,但是一般我们都知道需要部署服务器并启动http服务才能启动web服务呀,那Flask是怎么实现的呢? 下面研究一下run()的源码:
from werkzeug.serving import run_simple
try:
run_simple(host, port, self, **options)
finally:
# reset the first request information if the development server
# reset normally. This makes it possible to restart the server
# without reloader and that stuff from an interactive shell.
self._got_first_request = False
从上面的app.run()源码可以看出,run() 调用 werkzeug.serving 的 run_simple 方法,主动绑定IP,监听端口,调用程序等,开始提供服务。
那 Werkzeug 是什么?
Werkzeug 是一个 WSGI 工具包,也可以作为一个Web框架的底层库。WSGI 叫Web服务网关接口,用于定义Web服务器 和 Python Web程序通信的接口规范 ,是一种协议(规范)。
Web服务器在接受到请求之后,经过处理,可以通过WSGI将数据发给Python Web程序处理。如果没有 WSGI ,程序和服务器之间没有办法进行交互。

当客户端发出一个 HTTP 请求后,是如何转到Flask处理并返回的呢?
刚才我们用到的是是第一种架构,在这种结构里,uWSGI作为服务器,它用到了HTTP协议以及wsgi协议,Flask应用作为application,实现了wsgi协议。当有客户端发来请求,uWSGI接受请求,调用Flask app得到相应,之后相应给客户端。这里说一点,通常来说,Flask等web框架会自己附带一个WSGI服务器 (这就是Flask应用可以直接启动的原因,app.run()
启动了这个WSGI服务器),但是这只是在开发阶段用到的,在生产环境是不够用的,所以用到了uWSGI这个性能高的wsgi服务器。
WSGI:通信协议
uWSGI:uWSGI是一个全功能的HTTP服务器,实现了WSGI协议、uwsgi协议、http协议等。它要做的就是把HTTP协议转化成语言支持的网络协议。比如把HTTP协议转化成WSGI协议,让Python可以直接使用。
uwsgi:uWSGI服务器实现的独有的协议
下面来利用Werkzeug 来模拟 app.run()
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "hello, world!"
if __name__ == '__main__':
from werkzeug.serving import run_simple
run_simple('127.0.0.1', 5000, app, use_debugger=True)
接下来使用Werkzeug 来模拟 WSGI服务器 和 Flask实例化之后的APP 之间的数据交互的过程:
class myFlask(): # 自定义的,代替Flask类
def __call__(self, *args, **kwargs): # 必须是可回调的,这样uWSGI才知道调用那个程序
return 'hello, world!'
if __name__ == '__main__':
from werkzeug.serving import run_simple
app = myFlask()
run_simple('127.0.0.1', 5000, app, use_debugger=True)
运行之后发现,服务器可以启动,但是完成处理请求

这是因为WSGI 对于 application 对象有如下三点要求
- 必须是一个可调用的对象
- 接收两个必选参数environ、start_response
- 返回值必须是可迭代对象,用来表示http body
并且通过debug信息及源码可知,错误的地方是直接return数据(write()),并没有设置响应头。所以uWSGI没有办法通过app所得数据对发送响应。

调整代码:
class myFlask():
def __call__(self, environ, start_response):
#利用start_response定义请求头
start_response('200 ok', [('Content-Type', 'text/plain')])
#需要返回字节对象
return [b'hello, world!']
if __name__ == '__main__':
from werkzeug.serving import run_simple
app = myFlask()
run_simple('127.0.0.1', 5000, app, use_debugger=True)

利用上面的代码做个动态返回。
from werkzeug.wrappers import Request, Response
class myFlask():
def __call__(self, environ, start_response):
#使用Request来获取请求信息
request = Request(environ)
#根据请求报文所带的get参数u来动态回应,默认值是jk
text = 'hello {}'.format(request.args.get('u', 'jk'))
#使用Response来实例化响应对象
response = Response(text, mimetype="text/plain")
#返回响应对象
return response(environ, start_response)
if __name__ == '__main__':
from werkzeug.serving import run_simple
app = myFlask()
run_simple('127.0.0.1', 5000, app, use_debugger=True)


上面我们使用Werkzeug 实现了一个web 服务。可以看出,Werkzeug 为开发者提供与web服务器进行WSGI通信的类、方法等,有了它,Flask就具备提供web服务的能力,开发者只需要关注Flask app的开发即可。
最后来看一下不同web网关协议之间的关系:
CGI是鼻祖,FastCGI做了加强,WSGI针对Python程序做了优化




参考:
补充:
- uWSGI 服务器的 uwsgi 协议干嘛的?究竟用在何处?
https://www.zhihu.com/question/46945479
https://www.xncoding.com/2017/01/20/python/uwsgi.html
网友评论