资讯专栏INFORMATION COLUMN

浅析WSGI

王伟廷 / 2429人阅读

摘要:也就是说,是连接服务器和应用服务器的桥梁。目前实现的中,有两个角色分别是和。是一个可调用对象,它包含了一个表示响应状态的字符串和一个响应的列表以及一个用于出错返回的信息,具体参数包含及详情请点这里。可以被连接在一起,由此产生的链通常被称为。

首先,什么是WSGI?

WSGI, 全称为 Web Server Gateway Interface

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

其次,为什么PEP-0333觉得需要这个规范呢?

因为,就目前来看,Web服务一般按照如下方式部署:

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

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

WSGI是如何工作的?

通过上文,我们可以了解到WSGI无非做了两件事:

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

让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到底有多方便。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/37668.html

相关文章

  • Werkzeug Local与LocalProxy等浅析

    摘要:线程局部变量,也就是每个线程的私有变量,具有线程隔离性。按我们正常的理解,应该是每一个请求对应一个处理线程。在中,除了线程之外,还有个叫协程的东东,这里不提进程。配合会确保不管是协程还是线程,只要当前请求处理完成之后清除中对应的内容。 首先贴出官方文档地址:http://werkzeug.pocoo.org/doc...几个local?threading.localwerkzeug.l...

    The question 评论0 收藏0
  • Werkzeug 与 WSGI 介绍

    摘要:在介绍之前,先介绍一下,它为语言定义的服务器和应用程序或框架之间的一种简单而通用的接口。这个函数接受两个参数,分别是和。响应对象是一个应用,提供了更好的方法来创建响应。这部分解释来源于官方文档的中文版。 Werkzeug 是一个WSGI工具包,也可以作为一个Web框架的底层库。 WSGI 在介绍Werkzeug之前,先介绍一下 WSGI(Python Web Server Gate...

    mengera88 评论0 收藏0
  • 通读Python官方文档之wsgiref(未完成)

    摘要:一般来说,这一例行程序用于处理请求的每一部分,例如把路径作为一系列字典键值进行处理。,必须是按照中所规定地键值元组列表。行为时回车换行。这个包装器也可能用模块指明那些有问题的,但不完全违反的行为。 wsgirf-WSGI功能及参考实现 源码:Lib/wsgiref Web服务器网关接口(Web Server Gateway Interface, WSGI),是用Python写的一个服务...

    mumumu 评论0 收藏0
  • Python Web开发最难懂的WSGI协议,到底包含哪些内容?

    摘要:通过回调函数将响应状态和响应头返回给,同时返回响应正文,响应正文是可迭代的并包含了多个字符串。返回响应正文负责获取请求,将请求传递给,由处理请求后返回。 我想大部分Python开发者最先接触到的方向是WEB方向(因为总是有开发者希望马上给自己做个博客出来,例如我),既然是WEB,免不了接触到一些WEB框架,例如Django,Flask,Torando等等,在开发过程中,看过一些文档总会...

    atinosun 评论0 收藏0

发表评论

0条评论

王伟廷

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<