浅析WSGI

首先,什么是WSGI?

WSGI, 全称为 Web Server Gateway Interfacepython

它不是什么框架,它是一个规范,可是做为一个规范,它实际上并无官方标准。
这里要说明一下,PEP-0333并非用来规范WSGI用得,而是PEP-0333提出应该有那么一个东西用来处理web服务器和应用服务器之间的关系,所以产生了WSGI。nginx

其次,为何PEP-0333以为须要这个规范呢?

由于,就目前来看,Web服务通常按照以下方式部署:web

  1. 先部署一个web服务器(Apache etc.)用于处理协议层的业务,好比,单台物理机多服务(多域名或者多端口)、负载均衡等。apache

  2. 而后部署一个应用服务器(Django etc.),它用于处理具体业务方面的事情,好比,滴滴打车的应用服务器就会处理打车订单CRUD,处理完成以后呢再返回给web服务器,web服务器收到响应以后再返回给客户端。服务器

WSGI是如何工做的?

经过上文,咱们能够了解到WSGI无非作了两件事:app

  1. 让Web服务器知道如何调用Python应用程序并把从客户端来的请求拿过来。负载均衡

  2. 让Python应用程序知道客户端的具体请求是什么,以及如何返回结果给Web服务器并帮助Python应用程序把计算后的结果返回给Web服务器。框架

也就是说,WSGI是链接Web服务器和应用服务器的桥梁。测试

目前实现的WSGI中,有两个角色,分别是Server/Gatewayapplication/framework设计

当请求来临的时候,server调用application,而后application把结果返回给server

那么,server是如何调用application的?

接下来,server须要知道去哪可以找到application。实现这一逻辑,须要在server指定一个Python模块(具体在server的哪一个位置保存这一路径,那就得根据具体server类型来选择了,如apache或nginx)该模块必须包含一个名称为application的可调用对象,这个对象的形式以下:

PEP-0333中指出了,一个WSGI的application角色,应该是一个可调用对象:

def application(environ, start_response)

其中,environ是一个包含了关于此次HTTP请求信息的字典,start_response是一个可调用对象,用于在application中执行,用以在返回响应内容前设置响应的状态码和响应头,同时也意味着告诉serverapplication要开始返回http的body了。这两个就是server调用application时候须要传递的全部参数了。

那么,咱们还须要再说一下,environstart_response()是须要在server端的生成和定义的

有了这两个参数,application就能知道用户请求的是什么资源,请求中带了什么数据,结果该如何返回给server等等。其中,environ包含了一些符合CGI规范的环境变量和WSGI规范新添加的变量,此外还可能有一些系统变量及Web服务器相关的环境变量。start_response是一个可调用对象,它包含了一个表示HTTP响应状态的字符串和一个HTTP响应headers的列表以及一个用于出错返回的信息,具体参数包含及详情请点这里。

下面是一个完整的application demo,最终返回的body也能够是一个可迭代对象,这样server也能够经过遍历这个对象来拼接成body。

def application(environ, start_response):
    start_response("200 OK", [("Content-type", "text/plain")])
    return ["Hello World!",]

看上去不错,那么咱们该如何调用呢?

有几个服务器组件也可以运行的WSGI应用程序,可是对于简单的测试目的,咱们可使用包含在Python的标准库参考实现,就像下面那样:

if __name__ == '__main__':
    from wsgiref.simple_server import make_server
    server = make_server('localhost', 8080, application)
    server.serve_forever()

好了,还记得咱们刚才提到的最终返回的body也能够是一个可迭代对象吗?在python中,建立一个迭代器的简单办法就是使用一个生成器,好比,咱们有一个用于静态文件的服务,咱们能够写一个生成器,让它一次生成一个固定大小的文件块,这样咱们就能够随时一次只存储一个文件块了,让咱们来试试吧:

def send_file(file_path, size):
    with open(file_path) as f:
        block = f.read(BLOCK_SIZE)
        while block:
            yield block
            block = f.read(BLOCK_SIZE)

对应的 WSGI application 部分以下:

size = os.path.getsize(file_path)
headers = [
    ("Content-type", mimetype),
    ("Content-length", str(size)),
]
start_response("200 OK", headers)
return send_file(file_path, size)

注意,send_file就是上文中所指的可迭代对象。

WSGI中间件

看得出来,WSGI应用结构很是简洁,只需指定一个可调用的识别标记,这使得WSGI应用很容易实现调用其余框架,无非就是修改到来的请求或者修改发出的响应,固然也能够二者都有。那么,接下来看看demo:

class Filter(object):
    def __init__(self, application):
        self.application = application
        
    def __call__(self, environ, start_response):
        # Do something here to modify request
        pass
        
        # Call the wrapped application
        app_iter = self.application(environ, 
                                    self._sr_callback(start_response))
        
        # Do something to modify the response body
        pass
        
        # Return modified response
        return app_iter
        
    def _sr_callback(self, start_response):
        def callback(status, headers, exc_info=None):
            # Do something to modify the response status or headers
            pass
        
            # Call upstream start_response
            start_response(status, headers, exc_info)
        return callback

像这样应用一般被称为Middleware applicationsFilter Filter能够被链接在一块儿,由此产生的链一般被称为pipeline

最后呢,我想说明一下,WSGI是故意设计成最小的Web服务器实现轻松的应用,以便被更多的人采用。可是,几乎没有人真的喜欢直接操做environ变量,也几乎没有人喜欢用start_response这么诡异的逻辑,虽然WSGI提供的API易于实现,但这不表明它的语义让人满意。也正是由于这一缘由,几乎每个应用程序或者web框架都把environstart_response封装成了语义更完善容错率更高的request和response对象。

Webob就是request和response对象的规范实现之一,它使得WSGI更容易和更满意地被你们使用。

你能够在这里找到Webob的官方文档,固然,下次有机会我也会简单的说明一下Webob到底有多方便。

相关文章
相关标签/搜索