前面我们已经学习了编写基于TCP的Web服务器时使用的一些基本套接字和并发模式。HTTP协议的广为流行使得许多现成的解决方案应运而生,这些解决方案实现了我们可能需要的所有主要的服务器模式。因此,在使用HTTP时,我们几乎不太可能会编写任何底层的代码。
本章将着眼于服务器架构和部署,介绍编写服务器代码时需要解决的问题。无论是向客户端返回文档还是返回面向程序员的APi,这些问题都不可避免。
1.WSGI——Python网络服务的可移植性
在早期的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对象作为参数。在处理函数内部,可以调用apcahe模块的特殊函数来与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操作的标准方法。
根据标准的定义,WSGI应用程序是可以被调用的,并且有两个输入参数。下面这段代码展示了一个例子,其中使用一个简单的Python函数来表示可调用的WSGI程序。(也可以使用其他可调用类型,比如Python类甚至是包含__call__()方法的类实例。)第一个参数是environ,用于接收一个字典,字典中提供的键值对是旧式的CGI环境变量集合的扩展。第二个参数本身也是可以被调用的,习惯上会将其命名为start_response(),WSGI应用程序通过该方法来声明响应头信息。被调用后,app函数可以开始生成字节字符串(如果app函数本身是一个生成器),也可以返回一个可迭代对象。该对象可以在迭代过程中生成字节字符串(例如可以返回一个简单的Python列表)。
from pprint import pformat
from wsgiref.simple_server import make_server
def app(environ,start_response):
headers = {'Content-Type':'text/plain;charset=utf-8'}
start_response('200 OK',list(headers.items()))
yield 'Here is the WSGI environment:\r\n'.encode('utf-8')
yield pformat(environ).encode('utf-8')
if __name__ == '__main__&#