WSGI的介绍
由来
在早期的HTTP
编程中,许多Python
服务都是被编写为简单的CGI
脚本。每次收到请求就会出发CGI
脚本。服务器对HTTP
请求进行分割,将相应的参数以环境变量的形式传入CGI
脚本中。Python
程序员可以直接将这些输入参数和HTTP
响应打印到标准输出,也可以借助标准库的cgi
模块来查看。
在上面的方式会为每个接收到HTTP
请求启动一个新的进程。这大大影响了服务器的性能,因此各种语言开始着手实现自己的HTTP
服务器。Python
在标准库中加入了http.server
模块。使用该模块来实现服务时,程序员需要编写自己的子类继承BaseHTTPRequestHandler
,并添加do_GET()
和do_POST()
方法。
其一些程序员想在支持返回动态页面的Web
服务器上同事支持返回静态内容,因此出现了mod_python
。mod_python
是一个Apache
模块,允许成功注册的Python
函数提供自定义的Apache
处理函数,并且在自定义处理函数中提供认证和日志功能以及返回内容。mod_python
的API
是Apache
独有的。使用Python
编写的处理函数接受一个特殊的Apache request
对象。在处理函数内部,可以调用apache
模块的特殊函数来与Web
服务器进行交互。使用mod_python
的应用程序与使用CGI
或http.server
编写的程序几乎毫无相似之处。
因此,在Python
中使用上述不同方式编写HTTP
应用程序,在设计与Web
服务器交互的接口时都采用了某种特定的机制。使用CGI
方式编写的服务至少需要重写一部分代码才能应用http.server
。无论是使用CGI
还是http.server
编写的服务器程序,都需要经过修改才能够在Apache
下运行。这使得Python
网络服务的可移植性很差。
为了解决这一问题,Python
社区在PEP 333
中提出了Web
服务器网管接口(WSGI, Web Server Gateway Interface
)。而WSGI
标准就是添加了一层中间层。通过这一中间层,用Python
编写的HTTP
服务就能够与任何Web
服务器进行交互了。WSGI
标准指定了一个调用惯例,如果所有主流的Web
服务器的实现都遵循这一惯例,那么就能够直接在服务器中应用底层服务以及功能完整的Web
框架,而无需修改原来的代码。各大Web
服务器很快就遵循WSGI
进行了实现。现在,WSGI
已经成为了使用Python
进行HTTP
操作的标准方法。
一个简单的HTTP服务
1 | #!/usr/bin/env python3 |
根据标准的定义,WSGI
应用程序是可以被调用的,并且有两个输入参数。上面代码则是一个展示例子,其中使用一个简单的Python
函数来表示可调用的WSGI
应用程序。第一个参数是environ
,用于接受一个字典,字典中提供的键值对是旧式的CGI
环境变量集合的扩展。第二个参数本身是可以被调用的,习惯上会将其命名为start_response()
,WSGI
应用程序通过start_response()
来声明响应头信息。被调用后,app
函数可以开始生成字节字符串,也可以返回一个可迭代对象。该对象可以在迭代过程中生成字符串。
只看代码的话,你可能会觉得WSGI很简单,这是因为上面的代码只选择了一种简单的处理方式,没有展示出WSGI
标准的完整说明而已。实际编写服务器程序时,复杂度就大大提升了。如果想了解相关细节的话,可以阅读PEP 3333提出的现代Python3
版本的WSGI
。
发展
WSGI
出现后,WSGI
中间件的思想开始广为流传。未来Python
服务可能应该被设计为一系列WSGI
包装的函数。其中,有的包装函数会提供认证功能;有的会负责捕捉异常,记录日志;而有的则负责对指向仍然在机构中运行的老旧的CMS
的URL
进行反向代理,使用Diazo
将这些URL
重构,使之指向项目中最新页面。
尽管仍然有一些开发者会编写并使用WSGI
中间件,但是现在大多数Python
程序员使用WSGI
的主要原因是它提供应用程序或框架与监听HTTP
请求的Web
服务器之间的可插拔性。