Python 的 WSGI 简单了解
从 flask 的 hello world 说起
直接讨论 WSGI,很多人可能没有概念,我们还是先从一个简单的 hello world 程序开始吧。
from flask import Flaskapp = Flask(__name__)@app.route("/", methods=['GET'])
def index():return "Hello world!"if __name__ == '__main__':app.run(port=8000)
相信只要使用过 flask 框架或者其它 Python 框架的人,应该都会见过这个警告。这个警告的意思是:这是一个开发服务器。不要在生产环境使用它。使用一个生产的 WSGI 服务器来代替。
那么什么是 WSGI 呢?
WSGI is the Web Server Gateway Interface. It is a specification that describes how a web server communicates with web applications, and how web applications can be chained together to process one request.
WSGI 是 Web 服务器网关接口。它是一个规范,描述了web服务器如何与web应用程序通信,以及如何将web应用程序链接在一起以处理一个请求。
这里来看一个简略的时序图:
它描述了用户通过浏览器上网的过程,浏览器发送请求到 Server,Server 再把请求交给 Application 来处理,最后返回响应数据给用户的过程。(通常来说,请求在到达 Server 之前,还会先经过一个反向代理服务器,例如 nginx 或者 apache 等)
上面这个时序图其实可以看做是大部分网络应用程序的模式了。从请求到达应用和响应返回给用户,这个过程其实是固定的。所以,一个web应用的区别就在于它对请求的处理方式上,例如:用户使用百度、搜狗亦或是谷歌浏览器,对于用户的感知其实是相同的(尽管它们内部的实现并不相同,这里也不考虑用户的体验问题,哈哈)。
什么是 WSGI?
我们都知道通用的部分可以抽取出来,做成一个组件供其它应用使用。所以 WebServer 就是这样一个组件,它负责接收用户的请求,然后交给用户的Web应用,等到它处理完成之后,再把响应数据返回给调用者。所以,WebServer 要和 WebApplication 进行交互,那就需要定义一个协议或者更专业一点叫做接口,因此这就是 WSGI。而实现 WSGI 接口的,我们则成为 WebServer 或者 WSGI Server。
注:如果你有 java web 的背景,相信你应该使用过 tomcat,也听过 servlet API。其实,它们之间的关系和WSGI服务器与 WSGI接口的关系是类似的。
它的接口其实很简单,只要 Web 框架实现了它,就可以使用各种实现了 WSGI 接口的服务器了。
我们常用的 Web 框架有:falsk、django 等;WSGI 服务器有:gunicorn、uWSGI等。
对于我们入门学习来说,它其实也是很简单的,我们也不需要了解那么多。对于Web 框架的作者来说,他们需要提供一个带两个参数的可调用对象即可:
callable(environ, start_response)
下面是 Python 的 PEP3333 中提供的两个简单例子,一个是函数实现,一个是类实现,它们都是一个 Web Application。
HELLO_WORLD = b"Hello world!\n"def simple_app(environ, start_response):"""Simplest possible application object"""status = '200 OK'response_headers = [('Content-type', 'text/plain')]start_response(status, response_headers)return [HELLO_WORLD]class AppClass:"""Produce the same output, but using a class(Note: 'AppClass' is the "application" here, so calling itreturns an instance of 'AppClass', which is then the iterablereturn value of the "application callable" as required bythe spec.If we wanted to use *instances* of 'AppClass' as applicationobjects instead, we would have to implement a '__call__'method, which would be invoked to execute the application,and we would need to create an instance for use by theserver or gateway."""def __init__(self, environ, start_response):self.environ = environself.start = start_responsedef __iter__(self):status = '200 OK'response_headers = [('Content-type', 'text/plain')]self.start(status, response_headers)yield HELLO_WORLD
Python 官方提供了一个简单的 WSGI 实现,我们就以上面这两个例子为例来演示一下:
from wsgiref.simple_server import make_serverif __name__ == '__main__':with make_server("127.0.0.1", 8000, simple_app) as httpd:print("Server started on port 8000...")httpd.serve_forever()
使用基于类的实现,效果也是一样的,通常更推荐使用类的方式,因为可以利用面向对象的思想来编程。
from wsgiref.simple_server import make_serverif __name__ == '__main__':with make_server("127.0.0.1", 8000, AppClass) as httpd:print("Server started on port 8000...")httpd.serve_forever()
甚至,我们还可以再进行一步,使用这个 Python 自带的 WSGI 服务来运行最开始的 flask 的 hello world:
from wsgiref.simple_server import make_serverfrom flask import Flaskapp = Flask(__name__)@app.route("/", methods=['GET'])
def index():return "Hello world!"if __name__ == '__main__':with make_server("127.0.0.1", 8000, app) as httpd:print("Server started on port 8000...")httpd.serve_forever()
这样再次运行它,连警告也没有了。不过,自带的这个只是一个简单的实现,官方也并不推荐在生产环境使用它。但是 flask 其实并不知道,我们使用了什么 WSGI 服务器,因为它也不关心这个。
flask 背后的秘密
甚至不需要深入 flask 的源码,只需要浅浅的一探,我们就能明白为什么上面那样做的可以的了。这是 Flask 类的源码的一小部分,其它的无需解释了,我相信这是不言自明的。
class Flask(App):"""The flask object implements a WSGI application and acts as the centralobject. """def wsgi_app(self, environ: WSGIEnvironment, start_response: StartResponse) -> cabc.Iterable[bytes]:...def __call__(self, environ: WSGIEnvironment, start_response: StartResponse) -> cabc.Iterable[bytes]:"""The WSGI server calls the Flask application object as theWSGI application. This calls :meth:`wsgi_app`, which can bewrapped to apply middleware."""return self.wsgi_app(environ, start_response)
总结
关于 WSGI,它的文档就直接说明了,对于 Web 开发者来说,其实并不用去了解它。因为我们的工作重点其实是请求的处理,对于我上面序列图中的大部分操作都不需要关系了,它隐藏了很多的细节,使得我们可以更加专注于自己的工作内容。不过,我觉得简单的了解一下也是蛮好的,也不需要深入的了解,浅尝辄止即可。对于我们工作的整个领域都有一个概览,然后还是专注于自己工作的重点内容上。