博文 WSGI 简介 对 WSGI 说的很清楚:
WSGI的全称是Web Server Gateway Interface,翻译过来就是Web服务器网关接口。具体的来说,WSGI 是一个规范,
定义了 Web 服务器如何与 Python 应用程序进行交互
,使得使用 Python 写的 Web 应用程序可以和 Web 服务器对接起来。
上边已经说的很清楚,WSGI 就是用来规范 Web 服务器(如 Apache)如何与 Python 应用程序(如 Django)进行交互的。这个很重要,因为一般来说,服务器(程序)只有一个(专门处理 HTTP 请求),但应用程序却种类繁多,不可能让服务器为了某个应用程序而开发一套跟其交互的方案;而通过规范(类似 TCP/IP 协议),却能够让服务器与采用同样规范的应用程序连接起来,而不用单独考虑应用程序的特殊性。
上边提到的博客已经讲的非常好了,所以本文更多的是对 WSGI 的实践。另外一个要参考的资料是 Python 官方针对 WSGI 的提案 PEP 3333。这个方案对 WSGI 的各种细节参数都说明的非常详细,一定要仔细看。
WSGI 工作原理
根据前边提到的,WSGI 涉及到两个角色:服务器(称为 Server 或 Gateway)与应用程序(称为 Application 或 Framework)。服务器接收客户端的请求,然后根据请求调用相应的应用程序,应用程序处理完之后返回结果给服务器,服务器(经处理)再将结果返回给客户端。具体地,应用程序向服务端提供一个可调用的对象(callable object
,函数或类,是应用程序的入口),该对象(入口)接收服务端传入的两个参数(environ 和 start_response);服务端通过调用该对象(入口),从而实现对该应用程序的调用,如下图所示:
例如,我们可以来看一个例子:
1 | # -*- coding: utf-8 -*- |
另外,注意类也可以是一个应用程序的入口:
1 | # -*- coding: utf-8 -*- |
入口参数
对于一个应用程序来说,其标准入口定义如下(以函数为例):
1 | def simple_app(environ, start_response): # 这些参数都是服务端传给应用程序的 |
注:这些入口参数的名字不一定如上所述,只是这样叫是一个惯例,如 environ 也可为 environment。
下边我们逐一来解释一下。
environ
environ 是一个字典类型(!!!一定要是內建的)的对象,包含环境变量参数,由服务端传给应用,应用也可对其进行修改。
对该变量,博文 WSGI 简介已经说的很好了,下边介绍部分就直接摘录自该博文。
environ 字典包含了一些 CGI 规范要求的数据,以及 WSGI 规范新增的数据,还可能包含一些操作系统的环境变量以及 Web 服务器相关的环境变量。
首先是 CGI 规范中要求的变量:
- REQUEST_METHOD: 请求方法,是个字符串,’GET’, ‘POST’ 等
- SCRIPT_NAME: HTTP请求的path中的用于查找到application对象的部分,比如 Web 服务器可以根据 path 的一部分来决定请求由哪个 virtual host 处理
- PATH_INFO: HTTP 请求的 path 中剩余的部分,也就是 application 要处理的部分
- QUERY_STRING: HTTP 请求中的查询字符串,URL 中 ? 后面的内容
- CONTENT_TYPE: HTTP headers 中的 content-type 内容
- CONTENT_LENGTH: HTTP headers 中的 content-length 内容
- SERVER_NAME和SERVER_PORT: 服务器名和端口,这两个值和前面的 SCRIPT_NAME, PATH_INFO 拼起来可以得到完整的 URL 路径
- SERVER_PROTOCOL: HTTP 协议版本,HTTP/1.0 或者 HTTP/1.1
- HTTP_: 和 HTTP 请求中的 headers 对应。
WSGI 规范中还要求 environ 包含下列成员:
- wsgi.version:表示 WSGI 版本,一个元组 (1, 0),表示版本 1.0
- wsgi.url_scheme:http 或者 https
- wsgi.input:一个类文件的输入流,application 可以通过这个获取 HTTP request body
- wsgi.errors:一个输出流,当应用程序出错时,可以将错误信息写入这里
- wsgi.multithread:当 application 对象可能被多个线程同时调用时,这个值需要为 True
- wsgi.multiprocess:当 application 对象可能被多个进程同时调用时,这个值需要为 True
- wsgi.run_once:当server期望 application 对象在进程的生命周期内只被调用一次时,该值为 True
注:对于 CGI 的参数,可参考其官方草稿,也可参考 IBM 博文 CGI 脚本中的环境变量。
当然,我们也可以在程序中把 environ 的内容直接作为 body 返回,来查看 environ 的内容:
1 | # -*- coding: utf-8 -*- |
浏览器上输入 http://localhost:8051/ 就可以看到结果。
start_response
start_response 是一个可调用对象,用于返回应用的处理情况(status, headers)返回给服务端,表示应用要开始返回 body 了。
start_response 的定义如下:
1 | def start_response(status, response_headers, exc_info=None): |
其参数解释博文 WSGI 简介已经说的很好了:
- status: 一个字符串,表示 HTTP 响应状态字符串
- response_headers: 一个列表,包含有如下形式的元组:(header_name, header_value),用来表示 HTTP 响应的 headers
- exc_info(可选): 用于出错时,server 需要返回给浏览器的信息
当 application 对象根据 environ 参数的内容执行完业务逻辑后,就需要返回结果给server端。我们知道 HTTP 的响应需要包含 status,headers 和 body,所以在 application 对象将 body 作为返回值 return 之前,需要先调用 start_response(),将 status 和 headers 的内容返回给 server,这同时也是告诉 server,application 对象要开始返回 body 了。
中间件
除了服务端直接调用(最终)应用,我们还可以在服务端和应用之间添加中间件(Middleware),作为服务端与应用之间的中介,对服务端扮演“应用程序”角色,对应用扮演“服务器”角色
。它的工作流程如下图所示:
图片来源
现在,我们对文中前头提到的进行改造,添加一个中间件:
1 | # -*- coding: utf-8 -*- |
这种中间件的用处非常大,例如在 OpenStack 项目中就用其作为过滤器:
1 | # keystone/etc/keystone-paste.ini |
关于 PasteDeploy 我们将在下篇博文介绍。
URL 重建
利用 environ 参数,我们可以重建请求的 URL,如下:
1 | # -*- coding: utf-8 -*- |
浏览器输入 http://localhost:8051/ 可得到结果:http://localhost:8051/。
总结
我想,放一张大图就够了吧:
图片来源