WSGI(2)-PEP333理解

通过上一节,可以知道wsgi是什么,将里面的各种名词做了对比说明。PEP333是该协议的描述,具体说明了该协议的实现,这节将仔细了解该协议细节。PEP333中主要规定了wsgi中三个不同的组件的细节,分别是应用、服务器、中间件。
在分别说明它们之前需要明确应用程序被执行是通过服务器对应用程序的调用,这种调用可以有2种不同的理解,一个是调用形式或者说方式,一个是传递给服务器的方式。下图中说明了2种不同的传递方式和2种调用形式。

图中当一个请求过来时,服务器可以通过不同的方式获取应用程序的逻辑部分。图a中通过一个脚本,脚本中调用了server的api,将app的逻辑部分(fn或者obj)作为参数传递给server的api,服务器得到逻辑部分就可以执行。图b中则是需要配置config,而请求过来时server去config中读取逻辑部分的信息,然后再进行处理。调用形式主要是指一种具体的语法现象,比如上图中server中对函数调用的写法(fn(a,b)),类的写法(obj.method(a,b)),这是两种不同的调用形式。wsgi定义了这种调用形式,却没有规定服务器通过什么方式获取逻辑部分的信息,给了server自己实现的空间。规范中引入了一个名词callable,很多翻译称为可调用者。规范将其解释为一个函数、方法、类或者一个实现了__call__的实例。

we will use the term “a callable” to mean “a function, method, class, or an instance with a __call__ method”.

因为python里面函数、类、方法等各种实现都是对象,都可以作为参数进行传递和调用。

wsgi应用

通过以上的说明可以知道应用应该要提供一个可以给服务器调用的callable,另外该callable可以接受参数,并且调用后应该有返回,wsgi对此进行了规定:

  1. 应用程序对象要是一个可以接受2个位置参数的可调用对象。这里是位置参数,不是关键字参数,因此参数的命名也不是规定好的。
  2. 应用程序应该可以被多次调用。因为请求肯定是会重复的。

需要注意的是这里的应用程序对象只是指代了是应用程序端,并不是具体的应用程序,wsgi还是只在服务端和框架间做连接,不直接对应用程序编写提供支持,所以应用程序使用相应的框架就可以了,不需要通过api去调用wsgi。通过上面的描述可以知道应用在服务端的调用如下:
result = application(environ,start_response)
其中environ和start_response只是为了方便说明取的名字,实际中可以按自己需求取名字。
下面对参数好返回值进行说明:
1、environ
python中这是一个字典对象,是一个cgi风格的环境变量。这里要求必须是内置字典对象,不能派生。这里environ里面会包含一些wsgi所需的变量,也可以是服务器扩展的变量,具体的说明可以参考PEP333规范。
2、start_response
这个参数也是一个callable,用于在应用程序中调用。必须接受2个位置参数和1个可选参数。形式如下:
start_response(status,response_headers,exc_info=None)
其中,status参数形式是”200 message”,即状态码+message的字符串;
response_headers参数是一个元组,形式是(header_name,header_value),用于描述http响应头,例如

1
response_headers = [('Content-Type', 'text/plain'), ('Content-Length', str(len(response_body)))];

exc_info参数在捕捉到错误并需要在浏览器中显示的时候才需要。
其返回值要求返回一个write(body_data)的callable,body_data内容将被当成HTTP响应体的一部分输出。这里也不是一定需要返回write的,也可以什么不返回。
3、返回值
返回一个可迭代对象,能够生成0个或多个字符串。因此可以是列表,也可以是一个生成器,甚至可以应用程序本身就是一个可迭代的类的实例。如果是一个生成器,则需要实现一个close()方法,服务器端通过验证是否有close,在程序终止或者异常结束的时候调用,从而实现对生成器的支持。
返回的时候server会通过一种无缓冲的方式传送到客户端,并在传完一个字符串后再请求下一个字符串。服务器端通过len(iterable)来验证结果是否可以信任,因此返回值需要实现__len__()方法。服务器端一般不使用任何返回值的其它属性,除了是针对服务器的特殊情况。

wsgi服务器

服务器端的规范为:

  1. 需要有能够接受application的实现。比如提供一个接受application对象的api;
  2. 需要提供application需要的参数environ和start_response,从而调用application;
    application对start_response的调用只是看上去发起了一个响应,将响应头传送给客户端,实际上并不是,而是存储下来等待server来传送。当然还需要application调用返回了非空的字符串,或者应用程序调用中start_response的write方法可以被调用,即有了响应体数据时候才可以发送。这样使得在body完全生成之前,服务器还可以替换任何响应头信息,从而能够保证响应头没有问题。
    exc_info参数是一个元组。在start_response被错误处理的程序调用时需要提供,如果http响应头信息没有被发送则替换缓存的http响应头,从而输出错误信息,如果已经发送了http信息,start_response必须抛出错误信息,这样应用程序会捕获信息,但是由于如果返回了响应头后再次返回错误信息给浏览器的方式不安全,所以应该避免去捕获信息,而是将异常传给服务器。
  3. 能够处理application返回值,作为响应体返回给客户端。

中间件

因为中间件处于服务器和应用中间,而信息可以双向流动,所以中间件既可以扮演服务器加工服务器到应用的数据,也可以为扮演应用加工应用到服务器的数据。可以执行如下一些功能:

  1. 可以做路由转发,重写environ变量。
  2. 用于多个应用程序在进程中多线程运行。
  3. 做负载均衡。
  4. 对上下问加工,比如生成特定格式的数据。

整体说明

根据上面的说明,这里做一个整体的说明。

通过上图可以看出,request被server进行处理生成environ,然后将environ和start_response作为app的参数,调用app,app被调用后,再调用start_response参数,该参数会生成响应状态status和响应头header,此后app返回body,然后server将响应头、响应状态以及响应内容一起做为客户端的内容。