详解例说Paste模块(PasteDeploy)

使用PasteDeploy定制WSGI服务

一、为什么引入Paste模块?

(1)、当看到openstack的api-paste.ini中大量出现的composite、pipline、filter、app之后顿时感觉不淡定了,这些都是什么玩

   意?详细研究一番,却有意外的收获,明白了openstack使用这套东西的高明之处。

(2)、一个WSGI服务的核心部分,是它的应用程序。如果一个WSGI服务的应用程序修改了,那么WSGI服务相应的功能也就改变了。

   如果能够通过配置文件来配置WSGI服务的应用程序,那么,当需要对WSGI服务进行修改(需要添加删除某个功能模块)时,只需要

   简单的修改下配置文件,而不需要修改WSGI服务应用程序的代码。

这样子做的话,大大增强WSGI服务的伸缩性。PasteDeploy就是专门定制WSGI服务的开发包


二、PasteDeploy都有哪些玩意

Paste单词本意是:糊料,是不是想到粘合剂,其实就是粘合剂。

PasteDeploy定义的几类部件:

app(应用程序):WSGI服务的核心部分,用于实现WSGI服务的主要逻辑

         app是一个callable object,接受的参数(environ,start_response),这是paste系统交给application的,符合           WSGI规范的参数. app需要完成的任务是响应envrion中的请求,准备好响应头和消息体,然后交给start_response          处理,并返回响应消息体。

filter(过滤器):一般用于一些准备性的工作,例如验证用户身份、准备服务器环境等。在一个filter执行完之后,可以直接返 

         回,也可以交给下一个filter或者app继续执行。

         filter是一个callable object,其唯一参数是(app),这是WSGI的application对象,filter需要完成的

         工作是将application包装成另一个application(“过滤”),并返回这个包装后的application。

pipeline(管道):由若干个filter和1个app组成。通过pipeline,可以很容易定制WSGI服务

composite(复合体):用于实现复杂的应用程序,可以进行分支选择。例如:根据不同的URL调用不同的处理程序         

- app_factory是一个callable object,其接受的参数是一些关于application的配置信息:(global_conf,**kwargs),

 global_conf是在ini文件中default section中定义的一系列key-value对,而**kwargs,即一些本地配置,是在ini文件中,

 app:xxx section中定义的一系列key-value对。app_factory返回值是一个application对象

- filter_factory是一个callable object,其接受的参数是一系列关于filter的配置信息:(global_conf,**kwargs),

 global_conf是在ini文件中default section中定义的一系列key-value对,而**kwargs,即一些本地配置,是在ini文件中,

 filter:xxx section中定义的一系列key-value对。filter_factory返回一个filter对象


三、paste的目的与用心

paste的目的是什么,它要做什么?用过django,flask或者tornado的人一定知道,一个url一般与一个类或方法对应,以典型的

flask为例:  

@app.route('/')
def index():
    return 'Index Page'
@app.route('/hello')
def hello():
    return 'Hello World'
这里可以看到“/”对应了index() ,“/hello”对应了hello()。这里的装饰器被称为route。

其实单从URL上来说,是存在某种逻辑关系的,这种逻辑关系就像管道工程!

这种URL有可能必须经过某个分发(多通管道),又经过某个验证过程(过滤)之后再最终执行某个过程(应用)。


四、举例验证(自己慢慢体会,也是网上的例子,借鉴别人的,感觉很恰当)

管道中的液体,暂且假设为水,那么我们假设有如下URL,假设这是一家酒店:

/master_valve/hydrant        #/总阀/消防栓
/master_valve/tap           #/总阀/水龙头
/master_valve/boiler/shower    #/总阀/锅炉/花洒(淋浴房)
/master_valve/purifier/drinking_fountain    #/总阀/净化器/直饮水机

根据上面的URL我们用paste来描述出它们的逻辑关系。首先应该确定关键词:
composite:master_valve

filter:purifier,boiler

app:hydrant,tap,shower,drinking_fountain

按照这一逻辑,水从总阀中引出后经过管线进入一个多通管道(piprline),被分别引入消防栓,水龙头,净水器和锅炉。从净水器

引出后进入直饮水机(app),而从锅炉引出后进入淋浴房的花洒(app)。

   

代码如下:

(1)编写配置文件congigure.ini来描述这种逻辑关系。  

[app:hydrant]
paste.app_factory = hydrant:app_factory
in_arg = water

[app:tap]
paste.app_factory = tap:app_factory
in_arg = water

[app:shower]
paste.app_factory = shower:app_factory
in_arg = hot_water

[app:drinking_fountain]
paste.app_factory = drinking_fountain:app_factory
in_arg = pure_water

[filter:purifier]
paste.filter_app_factory = purifier:filter_app_factory
in_arg = water

[filter:boiler]
paste.filter_app_factory = boiler:filter_app_factory
in_arg = water

[pipeline:pip_to_shower]
pipeline = boiler shower

[pipeline:pip_to_drinking_fountain]
pipeline = purifier drinking_fountain

[composite:valve]
use = egg:Paste#urlmap
/hydrant = hydrant
/tap = tap
/boil/shower = pip_to_shower
/purifier/drinking_fountain = pip_to_drinking_fountain

[composite:main]
use = egg:Paste#urlmap
/master_valve: valve
里面有app、filter、pipeline、compisite,与之对应的是里面要确定对应的factory,如:

[app:tap]
paste.app_factory = tap:app_factory
in_arg = water

表示tap的paste.app_factory存在于tap.py的app_factory中,这是一个方法。

filter也是类似,只不过换成了paste.filter_app_factory,真正实现的时候注意app与factory的区别。

pipeline,主要起到组合的作用,将filter(过滤器)和app(应用)组合起来,形成一条管线。

[composite:valve]
use = egg:Paste#urlmap
/hydrant = hydrant
/tap = tap
/boil/shower = pip_to_shower
/purifier/drinking_fountain = pip_to_drinking_fountain
Paste#urlmap 表示,默认使用Paste.urlmap。  http://pythonpaste.org/deploy/

use = egg:Paste#urlmap意味着直接使用来自于Paste包的urlmap的composite应用。 urlmap是特别常见的composite应用——它使

用路径前缀来映射将你的请求与其他应用对应起来。基本含义就是说,这是Paste已经提供好的一个composite,如果你想自定义就

需要另外写一个composite_factory了。

(2)依次实现里面的类

从总阀开始说起:

#master_valve.py
#apt-get install python-paste
#apt-get install python-PasteDeploy

from paste import httpserver
from paste.deploy import loadapp

if __name__ == '__main__':
    httpserver.serve(loadapp('config:configure.ini', relative_to = '.'), host = '127.0.0.1', port=8000)

或者

from wsgiref.simple_server import make_server
from paste import httpserver
from paste.deploy import loadapp
import os

if __name__ == '__main__':
    configfile = 'configure.ini'
    appname = 'main'
    wsgi_app = loadapp('config:%s' % os.path.abspath(configfile), appname)

    #httpserver.serve(loadapp('config:configure.ini', relative_to = '.'), host = '127.0.0.1', port=8000)

    server = make_server('localhost', 8000, wsgi_app)
    server.serve_forever()

httpserver在loadapp中将配置文件载入,并绑定在8000端口上。


下面是四个应用(app):

水龙头

#tap.py
class Tap(object):
    def __init__(self, in_arg):
        self.in_arg = in_arg

    def __call__(self, environ, start_response):
        print 'Tap'
        start_response('200 ok', [('Content-Type', 'text/plain')])
        return ['%s, %s!\n' % (self.in_arg, 'Tap')]

def app_factory(global_config, in_arg):
    return Tap(in_arg)
花洒(淋浴房)

#shower.py
class Shower(object):
    def __init__(self, in_arg):
        self.in_arg = in_arg

    def __call__(self, environ, start_response):
        print 'Shower'
        start_response('200 ok', [('Content Type', 'text/plain')])
        return ['%s, %s!\n' % (self.in_arg, 'Shower')]

def app_factory(global_config, in_arg):
    return Shower(in_arg)
消防栓:

#hydrant.py
class Hydrant(object):
    def __init__(self, in_arg):
        self.in_arg = in_arg

    def __call__(self, environ, start_response):
        print 'Hydrant'
        start_response('200 ok', [('Content Type', 'text/plain')])
        return ['%s, %s!\n' % (self.in_arg, 'Hydrant')]

def app_factory(global_config, in_arg):
    return Hydrant(in_arg)

直饮水机:

#drinking_fountain
class DrinkingFountain(object):
    def __init__(self, in_arg):
        self.in_arg = in_arg

    def __call__(self, environ, start_response):
        print 'DrinkingFountain'
        start_response('200 ok', [('Content Type', 'text/plain')])
        return ['%s, %s!\n' % (self.in_arg, 'DrinkingFountain')]

def app_factory(global_config, in_arg):
    return DrinkingFountain(in_arg)

应用里面的结构是类似的,需要注意factory方法的参数,这会与后面的filter的factory有所不同。

每一个应用类内部的__init__和__call__方法是需要自己实现的,__call__方法返回的字符串将显示在web界面上,

start_response(status, response_headers)语句及其涉及的参数前面已经介绍。

应用部分结束。


下面是过滤器,总共两个过滤器,分别是锅炉和净水器:

锅炉:

#boiler.py
class Boiler(object):
    def __init__(self, app, in_arg):
        self.app = app
        self.in_arg = in_arg

    def __call__(self, environ, start_response):
        print 'Boiler'
        return self.app(environ, start_response)
净水器:

#purifier.py
class Purifier(object):
    def __init__(self, app, in_arg):
        self.app = app
        self.in_arg = in_arg

    def __call__(self, environ, start_reponse):
        print 'Purifier'
        return self.app(environ, start_response)

def filter_app_factory(app, global_config, in_arg):
    return Purifier(app, in_arg)

可以看到,在实现过滤器factory函数的时候,必须要将app参数带入,且必须是第一个参数。

实际上这里过滤器并没有真正过滤什么,实际应该在__call__中进行一定的处理,设定一定的条件,将一定的条件拦截。

输出结果:

执行程序:

python master_valve.py



可以看到:


此时,水经过锅炉,再进入花洒,执行了两个APP。


总结:此篇博文下来Paste.Deploy了解的七七八八,还有几个不明确的地方

(1)、载入配置文件之后的各个类传入的参数

(2)、composite各个路径之间的组合不是很清楚

等后面再深入的时候,带着这些问题往后面看,相信会更清楚的。

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值