天天看点

Openstack Restful API 开发框架 Paste + PasteDeploy + Routes + WebObPaste + PasteDeploy + Routes + WebOb 简介WSGI入口Paste和PasteDeploy中间件的实现RoutesWebOb

由 paste + pastedeploy 完成 application 的 wsgi 化,其中 pastedeploy 完成 wsgi server 和 application 的构建;

routes 负责 url 路由转发;

webob 完成了 wsgi 请求和响应的封装 。

使用该框架开发出来的 restful api 能够满足 wsgi 规范的要求,但是弊端在于该框架比较复杂,代码量大。只有最初的几个 openstack 核心项目在使用,后来的新生项目使用了一个相对而言更加简单便捷的 pecan 框架。

restful api 程序的主要特点就是 url_path 会和功能对应起来。比如用户管理的功能一般都放在 <code>http://hostname:post/version/&lt;project_id&gt;/user</code> 这个路径下。因此,看一个 restful api 程序,一般都是看它实现了哪些 ur_path,以及每个 url_path 对应了什么功能,这个一般都是由 routes 的 url 路由功能负责的。所以,熟悉一个restful api程序的重点在于确定url路由。

wsgi 可以使用 apache 充当 web server,也可以使用 eventlet 进行部署,keystone project 都提供了这两种方案的代码实现(也就是 wsgi 的入口)。keystone project 在 <code>keystone/httpd/</code> 目录下,存放了 apache web server 用于部署 wsgi 的相关文件:

wsgi-keystone.conf 是 mod_wsgi 功能模块的示例配置文件,其中定义了 post :5000 和 post :35357 两个虚拟主机,可以通过 request url_path 中 post 的不同来指定使用哪一个虚拟主机的 wsgi application 来处理这一个请求。

keystone.py 则是 wsgi application 的入口文件,web server 会根据配置路径来加载这个入口文件,在该文件中包含了由 wsgi module(wsgi.py) 提供的 application 可调用对象,该对象就是 wsgi application 真正的入口。

apache 部署方式的 wsgi 入口 :

keystone/httpd/keystone.py 调用了 keystone/keystone/server/wsgi.py 模块中的 <code>initialize_application(name)</code> 函数来载入 wsgi application,这里主要用到了 paste + pastedeploy 库。

name很关键

<code>name = os.path.basename(__file__)</code> 这个变量从 keystone/httpd/keystone.py 文件传递到 <code>initialize_application()</code> 函数,再被传递到 <code>keystone_service.loadapp()</code> 函数,最终被传递到 <code>paste.deploy.loadapp()</code> 函数。name 是用来确定application 入口的,表示了配置文件 paste.ini 中一个 composite section 的名字,指定这个 section 作为处理 http request 的第一站。在 keystone 的 paste.ini 中,请求必须先由 <code>[composite:main]</code> 或者 <code>[composite:admin]</code> 处理,所以在 keystone 项目中,name 的值必须是 main 或者 admin 。

上面提到的 keystone/httpd/keystone.py 文件中,name 等于文件名的 basename,所以实际部署中,必须把 keystone.py 重命名为 main.py 或者 admin.py ,这样<code>os.path.basename(__file__)</code>的值才能为 main 或 admin 。

eventlet 部署方式的入口:

keystone-all 指令则是采用 eventlet 来进行部署时,可以从 setup.cfg 文件中确定 keystone-all 指令的入口。

main() 函数的内容:

使用paste和pastedeploy模块来实现 wsgi services 时,都需要加载一个 paste.ini 文件。这个文件也是 paste 模块的精髓,那么这个文件如何阅读呢 ?

paste.ini 文件的格式类似于ini格式,每个 section 的格式均为 <code>[type:name]</code>。

这里重要的是理解几种不同 type 的 section 的作用:

composite : 这种 section 用于将 http url request 分发到指定的 application 。

keystone 的 paste.ini 文件中有两个 composite 的 section:

注意:在 virtualenv 环境下,是到文件 /lib/python2.7/site-packages/paste-2.0.2.dist-info/metadata.json 下去寻找 urlmap 关键字所对应的函数,而非 egg-info :

注意:在不同的版本中可能会有不同的 composite section 实现,example-m版:

以[composite:main]为例,看看其他关键字的作用:

pipeline : 用来把一系列的 filter 过滤器和 app 串起来。

它只有一个关键字就是 pipeline。example:

我们以api_v3这个pipeline为例:

filter: 实现一个过滤器中间件。

filter(以wsgi中间件的方式实现)是用来过滤请求和响应的,应该不同的中间件的过滤,该请求就具有了不同的功能。example:

app: 一个 app 就是一个实现主要功能的具体的 wsgi application 。

paste.ini 配置文件中这一大堆配置的作用就是把我们用 python 写的 wsgi application 和 middleware 串起来,规定好 http request 处理的路径。当 paste + pastedeploy 模块提供的 wsgi server(web server) 接受到 url_path 形式的 http request 时,这些 request 首先会被 paste 按照配置文件 paste.ini 进行处理,处理的过程为:composite(将request转发到pipeline或app) ==&gt; pipeline(包含了filter和app) ==&gt; filter(调用middleware对request进行过滤) ==&gt; app(具体的application来实现request的操作) 。这个过程就是将 application 和 middleware 串起来的过程 。

举个例子:

一般情况下,如果希望从 keystone service 获取一个 token 时,会使到 <code>http://hostname:35357/v3/auth/tokens</code> 这个 api。

我们根据 keystone 的 paste.ini 配置文件来说明这个 api 是如何被处理的:

step1. (hostname:35357): 这一部分是由 apache web server 来获取并处理的。然后,请求会被 web server 转到 wsgi application 的入口,也就是 keystone/httpd/keystone.py 中的 application 对象取处理。

step2. (/v3/auth/tokens): application 对象根据 paste.ini 中的配置来对剩下的(/v3/auth/tokens)部分进行处理。首先请求的 post=35357 决定了会经过 <code>[composite:admin]</code> section 。(一般是admin监听35357端口,main监听5000端口,也会受到 name 变量的影响)

step3. (/v3): 决定将请求路由到哪一个 pipeline secion,<code>[composite:admin]</code> 发现请求的 url_path 是 /v3 开头的,于是就把请求转发给<code>[pipeline:api_v3]</code>处理,转发之前,会把 /v3 这个部分去掉。

step4. (/auth/tokens) : <code>[pipeline:api_v3]</code>收到请求,url_path是 (/auth/tokens),然后开始调用各个 filter(中间件) 来处理请求。最后会把请求交给<code>[app:service_v3]</code>进行处理。

step5. (/auth/tokens): <code>[app:service_v3]</code> 收到请求,url_path是 (/auth/tokens),最后交由的 wsgi application 去处理。

到此为止,paste.ini 中的配置的所有工作都已经做完了。下面请求就要转移到最终的 application 内部去处理了。

注意:那么通过 paste.ini 处理过后,剩下的一部分url_path(/auth/tokens)的路由还没确定,它又是怎么被处理的呢?

middleware 中间件在 paste.ini 配置文件中以 filter section 的形式被表示,example:

build_auth_context 这个 filter (中间件)的作用是在 wsgi application 需要接受的 environ 环境变量参数中添加 <code>keystone_auth_context</code> 这个成员key,包含的内容是认证信息的上下文。这个 filter 的类继承关系为:

<code>class keystone.common.wsgi.middleware</code> 实现了<code>__call__()</code>方法,是 application 对象被调用时运行的方法。

<code>__call__()</code>内部调用的 <code>self.process_request()</code> 在 keystone.middleware.core.authcontextmiddleware 中实现:

对于url_path是以 /v3 开头的请求,在 paste.ini 中会被路由到 <code>[app:service_v3]</code> 这个 app section,并且交给 <code>keystone.service:v3_app_factory</code>这个函数生成的 application 处理。最后这个 application 需要根据 url_path 中剩下的部分(/auth/tokens),来实现 url 路由。这需要使用 routes 模块。

routes module:是用 python 实现的类似 rails 的 url 路由系统,它的主要功能就是把接收到的 url_path 映射到对应的操作。routes 的一般用法是创建一个 mapper 对象,然后调用该 mapper 对象的 <code>connect()</code> 方法把 url_path 和 http 内建方法映射到一个 controller 的某个 action 上。

这里 controller 是一个自定义的类实例,每一个资源都对应一个 controller,是对资源操作方法的集合。action 表示 controller 对象提供的操作方法(eg. index/show/delect/update/create )。一般调用这些 action 的时候会把这个 action 映射到 http 的内置方法中去。eg. get/post/delete/put 。

example:

_add_resource的实现:

总结:url_path(/auth/tokens) 和 http 内建的方法,在 routes module 中被 mapper 对象的 connect 方法映射到某一个 controller 的 action 操作函数中。实现了调用 url_path 即调用 action 操作函数的效果,而且因为 http 的内建方法也被映射在其中,所以可以很容易的使用 http 协议来实现操作。

webob 有两个重要的对象:

一个是 webob.request,对 wsgi request 的 environ 参数进行封装。

一个是 webob.response ,包含了标准 wsgi response 的所有元素。

此外,还有一个 webob.exc,针对 http 错误代码进行封装。

除了这三种对象,webob还提供了一个装饰器webob.dec.wsgify,以便我们可以不使用原始的 wsgi 参数传递和返回格式,而全部使用 webob 替代。

原始方式调用:

webob调用方式: