WSGI的全称是Web Server Gateway Interface,翻译过来就是Web服务器网关接口。具体的来讲,WSGI是一个规范,定义了Web服务器如何与Python应用程序进行交互,使得使用Python写的Web应用程序能够和Web服务器对接起来。WSGI一开始是在PEP-0333中定义的,最新版本是在Python的PEP-3333定义的。python
对于初学者来讲,上面那段就是废话,说了跟没说同样。本文的主要内容就是说清楚,WSGI究竟是如何工做的。nginx
在Web部署的方案上,有一个方案是目前应用最普遍的:web
首先,部署一个Web服务器专门用来处理HTTP协议层面相关的事情,好比如何在一个物理机上提供多个不一样的Web服务(单IP多域名,单IP多端口等)这种事情。浏览器
而后,部署一个用各类语言编写(Java, PHP, Python, Ruby等)的应用程序,这个应用程序会从Web服务器上接收客户端的请求,处理完成后,再返回响应给Web服务器,最后由Web服务器返回给客户端。服务器
那么,要采用这种方案,Web服务器和应用程序之间就要知道如何进行交互。为了定义Web服务器和应用程序之间的交互过程,就造成了不少不一样的规范。这种规范里最先的一个是CGI][3,1993年开发的。后来又出现了不少这样的规范。好比改进CGI性能的FasgCGI,Java专用的Servlet规范,还有Python专用的WSGI规范等。提出这些规范的目的就是为了定义统一的标准,提高程序的可移植性。在WSGI规范的最开始的PEP-333中一开始就描述了为何须要WSGI规范。架构
从上文能够知道,WSGI至关因而Web服务器和Python应用程序之间的桥梁。那么这个桥梁是如何工做的呢?首先,咱们明确桥梁的做用,WSGI存在的目的有两个:app
让Web服务器知道如何调用Python应用程序,而且把用户的请求告诉应用程序。框架
让Python应用程序知道用户的具体请求是什么,以及如何返回结果给Web服务器。函数
在WSGI中定义了两个角色,Web服务器端称为server或者gateway,应用程序端称为application或者framework(由于WSGI的应用程序端的规范通常都是由具体的框架来实现的)。咱们下面统一使用server和application这两个术语。性能
server端会先收到用户的请求,而后会根据规范的要求调用application端,以下图所示:
调用的结果会被封装成HTTP响应后再发送给客户端。
首先,每一个application的入口只有一个,也就是全部的客户端请求都同一个入口进入到应用程序。
接下来,server端须要知道去哪里找application的入口。这个须要在server端指定一个Python模块,也就是Python应用中的一个文件,而且这个模块中须要包含一个名称为application的可调用对象(函数和类均可以),这个application对象就是这个应用程序的惟一入口了。WSGI还定义了application对象的形式:
def simple_app(environ, start_response): pass
上面代码中的environ
和start_response
就是server端调用application对象时传递的两个参数。
咱们来看具体的例子。假设咱们的应用程序的入口文件是/var/www/index.py
,那么咱们就须要在server端配置好这个路径(如何配置取决于server端的实现),而后在index.py
中的代码以下所示:
使用标准库(这个只是demo)
import wsgiref application = wsgiref.simple_server.demo_app
使用web.py框架
import web urls = ( '/.*', 'hello', ) class hello(object): def GET(self): return "Hello, world." application = web.application(urls, globals()).wsgifunc()
你能够看到,文件中都须要有一个application对象,server端会找到这个文件,而后调用这个对象。因此支持WSGI的Python框架最终都会有这么一个application对象,不过框架的使用者不须要关心这个application对象内部是如何工做的,只须要关心路由定义、请求处理等具体的业务逻辑。
由于application对象是惟一的入口,因此无论客户端请求的路径和数据是什么,server都是调用这个application对象,具体的客户端请求的处理有application对象完成。
上面已经提到了,application对象须要是一个可调用对象,并且其定义须要知足以下形式:
def simple_app(environ, start_response): pass
当server按照WSGI的规范调用了application以后,application就能够开始处理客户端的请求了,处理请求以后,application对象须要返回处理结果给server端。处理请求和返回结果这两个事情,都和server调用application对象时传递的两个参数有关。
environ参数是一个Python的字典,里面存放了全部和客户端相关的信息,这样application对象就能知道客户端请求的资源是什么,请求中带了什么数据等。environ字典包含了一些CGI规范要求的数据,以及WSGI规范新增的数据,还可能包含一些操做系统的环境变量以及Web服务器相关的环境变量。咱们来看一些environ中经常使用的成员:
首先是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
上面列出的这些内容已经包括了客户端请求的全部数据,足够application对象处理客户端请求了。
start_response是一个可调用对象,接收两个必选参数和一个可选参数:
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了。
application对象的返回值用于为HTTP响应提供body,若是没有body,那么能够返回None。若是有body的化,那么须要返回一个可迭代的对象。server端经过遍历这个可迭代对象能够得到body的所有内容。
PEP-3333中有一个application的实现demo,我把它再简化以后以下:
def simple_app(environ, start_response): status = '200 OK' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) return ['hello, world']
能够将这段代码和前面的说明对照起来理解。
前面已经知道server如何定位到application的入口了,也知道了application的入口的形式以及application对象内部须要完成的工做。那么,咱们还须要再说一下,environ
和start_response()
是须要在server端的生成和定义的,其中关于start_response()
的部分在规范中也有明确的要求。这部份内容太长了,不适合放在本文中,有兴趣的读者能够去看下PEP-3333,里面有一段server端的demo实现。
WSGI Middleware(中间件)也是WSGI规范的一部分。上一章咱们已经说明了WSGI的两个角色:server和application。那么middleware是一种运行在server和application中间的应用(通常都是Python应用)。middleware同时具有server和application角色,对于server来讲,它是一个application;对于application来讲,它是一个server。middleware并不修改server端和application端的规范,只是同时实现了这两个角色的功能而已。
咱们能够经过下图来讲明middleware是如何工做的:
上图中最上面的三个彩色框表示角色,中间的白色框表示操做,操做的发生顺序按照1 ~ 5进行了排序,咱们直接对着上图来讲明middleware是如何工做的:
Server收到客户端的HTTP请求后,生成了environ_s
,而且已经定义了start_response_s
。
Server调用Middleware的application对象,传递的参数是environ_s
和start_response_s
。
Middleware会根据environ
执行业务逻辑,生成environ_m
,而且已经定义了start_response_m
。
Middleware决定调用Application的application对象,传递参数是environ_m
和start_response_m
。Application的application对象处理完成后,会调用start_response_m
而且返回结果给Middleware,存放在result_m
中。
Middleware处理result_m
,而后生成result_s
,接着调用start_response_s
,并返回结果result_s
给Server端。Server端获取到result_s后就能够发送结果给客户端了。
从上面的流程能够看出middleware应用的几个特色:
Server认为middleware是一个application。
Application认为middleware是一个server。
Middleware能够有多层。
由于Middleware能过处理全部通过的request和response,因此要作什么均可以,没有限制。好比能够检查request是否有非法内容,检查response是否有非法内容,为request加上特定的HTTP header等,这些都是能够的。
要使用WSGI,须要分别实现server角色和application角色。
Application端的实现通常是由Python的各类框架来实现的,好比Django, web.py等,通常开发者不须要关心WSGI的实现,框架会会提供接口让开发者获取HTTP请求的内容以及发送HTTP响应。
Server端的实现会比较复杂一点,这个主要是由于软件架构的缘由。通常经常使用的Web服务器,如Apache和nginx,都不会内置WSGI的支持,而是经过扩展来完成。好比Apache服务器,会经过扩展模块mod_wsgi来支持WSGI。Apache和mod_wsgi之间经过程序内部接口传递信息,mod_wsgi会实现WSGI的server端、进程管理以及对application的调用。Nginx上通常是用proxy的方式,用nginx的协议将请求封装好,发送给应用服务器,好比uWSGI,应用服务器会实现WSGI的服务端、进程管理以及对application的调用。