中科院云平台架构师,专注于数字化、智能化,技术方向:云、Linux内核、AI、MES/ERP/CRM/OA、物联网、传感器、大数据、ML、微服务。
分类: 云计算
2015-01-10 11:13:46
paste.deploy
用来解析/etc/nova/api-paste.ini文件,加载用于服务的wsgi app(Web服务器网关接口(Python Web Server Gateway Interface)是Python应用程序或框架和Web服务器之间的一种接口,wsgi app ,又称 应用 ,就是一个WSGI application。 wsgi containe容器)。它的功能有:
1:api-paste.ini中配置多个wsgi app(es2,osapi-compute,metadata),deploy可根据传入的app name加载指定的wsgi app;
2:通过写入api-paste.ini的配置,可方便地实现特定字符开始的url到特定wsgi app的映射。如:
[composite:osapi_compute]
use = call:nova.api.openstack.urlmap:urlmap_factory
/: oscomputeversions
/v2: openstack_compute_api_v2通过该配置,以“/v2”开始的url将交给名为openstack_compute_api_v2的WSGI APP处理,其它以“/”开的url就交给oscomputerversions处理。其实这并非deploy的功能,而是上面配置的urlmap实现 的。不过通过配置文件,再由deploy调用urlmap,使用就更简单了。
3:middle ware的简单加载和去除。
[composite:openstack_compute_api_v2]
use = call:nova.api.auth:pipeline_factory
keystone = faultwrap sizelimit authtoken keystonecontext ratelimit osapi_compute_app_v2上面的faultwrap sizelimit authtoken keystonecontext ratelimit都为middle ware(在nova代码中称为MiddleWare,deploy中称为filter),osapi_compute_app_v2才是wsgi app。请求先交给middle ware处理,再由middle ware决定要不要或者怎么交给wsgi app处理。这些middle ware是可以添加和去除的,如果不想对osapi_compute_app_v2进行限速,那么去掉ratelimit就可以了。其实这是 pipeline_factory实现的功能,不过通过deploy来配置加载更方便。
nova-api中提供了很多服务API:ec2(与EC2兼容的API),osapi_compute(openstack compute自己风格的API),osapi_volume(openstack volume服务API),metadata(metadata 服务API)等。通过配置文件api-paste.ini,可以方便管理这些API。
deploy提供了server、filter、app的概念,其中filter(即middle ware)、app在nova-api被重度使用,的确太好用了。发布每个API时,它们有时需要一些相同的功能,如:keystone验证、限速、错误 处理等功能。nova将这些功能实现为middle ware,如果需要,通过api-paste.ini配置,给它加上就可以。
比如,我需要记录每个访问nova-api的ip,及它们的访问次数和访问的 时间。那么我实现一个middle ware--nova.api.middleware:MonitorMiddleware来记录这些数据。通过下面的api-paste.ini配置, 就可对nova-api的访问进行记录了。
[composite:openstack_compute_api_v2]
use = call:nova.api.auth:pipeline_factory
keystone = faultwrap sizelimit authtoken keystonecontext ratelimit monitor osapi_compute_app_v2[filter:monitor] paste.filter_factory = nova.api.middleware:MonitorMiddleware.factory
webob
用于对wsgi app进行封装,简化wsgi app的定义与编写。webob主要提供三种功能。
1 Request。该类用于对传给wsgi app的env参数进行封装,简化对HTTP请求参数的访问和设置。这种简化体现在(假设用env对Request实例化了一个对象req):
1) 使用间接明了的属性名对HTTP参数进行访问,req.method可获取HTTP请求方法(替代REQUEST_METHOD);req.scheme 可获取HTTP请求协议http or https(替代wsgi.url_scheme);req.body获取请求body(替代wsgi.input)。
2)大量使用 property,省去繁琐细节,简化HTTP参数的访问和设置。req.body直接访问HTTP请求的body,而不用考虑(body的长度和字符编 码);req.POST以字典形式返回POST请求的参数;req.GET以字典形式返回GET请求的参数。
nova.api.openstack.wsgi.Request继承该类,并加了一个缓存已访问db的记录的功能,和对content_type判断和检测的功能。
2 Response。该类用于对HTTP的返回值进行封装。与Request对象类似,同样使用了property简化对http参数的访问和设置。支持 wsgi app一次返回status和body,这样更直观。其实Response实例本身也是一个wsgi app。
3 decorator--wsgify,装饰wsgi app,使其可以以如下方式定义:
其 中参数req是一个Request(默认)或其子类(通过wsgify(RequestClass=XXX)指定)的实例,是用env初始化的。 req.Response默认为webob.Response。以该种方式定义的wsgi app,其结果可以以三种形式返回:
1)返回一个字符串。wsgify将其作为body,并加上一些默认的参数,如status=“200 OK", content_type, content_length等,构造成一个HTTP响应结果并返回;
2)返回一个Response实例,直接返回该resp代表的HTTP请求结果;
3)返回一个wsgi app,wsgify会继续调用该app,并返回app的响应结果。
nova.wsgi.Router就是用第三种返回形式,两次返回wsgi app,最终将HTTP请求根据url映射到对应的controller处理。
routes
用来给服务内部定义url到具体函数的映射。deploy也有url到服务映射功能,但这个映射层次偏高一点。根据上面配置,deploy将以 “/v2”开始的url将交给名为openstack_compute_api_v2处理。但openstack_compute_api_v2怎么将 /v2/project_id/servers/的GET请求交给 nova.api.openstack.compute.servers.Controller.index()处理,并且将POST请求交给 create()处理呢;怎么将/v2/project_id/servers/id的GET请求交给show()处理呢?这个就是 routes.mappers所提供的功能,它根据path和请求方法,将请求映射到具体的函数上。如在nova中,添加/v2/project_id /servers/{list_vm_state, os_vmsum}两个GET请求来分别获取指定VM的状态和VM的总数。可在 nova.api.openstack.compute.APIRouter中添加如下两行,将请求分别交给list_vm_state和 os_vmsum两个函数处理并返回结果:
主题--nova-api服务流程分析
上面介绍了nova-api发布所用到的一些lib库,有了上面的基础知识,再来分析nova-api的发布流量,就比较轻松了。
nova-api可以提供多种api服务:ec2, osapi_compute, osapi_volume, metadata。可以通过配置项enabled_apis来设置启动哪些服务,默认情况下,四种服务都是启动的。
从nova-api的可执行脚本中,可以看出每个nova-api服务都是通过nova.service.WSGIService来管理的:
从上可知,WSGIService使用self.app = self.loader.load_app(name)来加载wsgi app,app加载完成后,使用nova.wsgi.Server来发布服务。Server首先用指定ip和port实例化一个监听socket,并使用 wsgi.server以协程的方式来发布socket,并将监听到的http请求交给app处理。对于Server的启动过程,代码上理解还是比较简单 的,没多少分析的。下面我们主要来分析处理HTTP请求的wsgi app是如何构建的,对于每一个请求,它是如何根据url和请求方法将请求分发到具体的具体函数处理的。
上个语句self.loader.load_app(name)中的loader是nova.wsgi.Loader的实例。Loader.load_app(name)执行下面指令,使用deploy来加载wsgi app:
self.config_path为api-paste.ini文件路径,一般为/etc/nova/api-paste.ini。name为ec2, osapi_compute, osapi_volume, metadata之一,根据指定的name不同来加载不同的wsgi app。下面以name=“osapi_compute”时,加载提供openstack compute API服务的wsgi app作为具体分析。osapi_compute的配置如下
[composite:osapi_compute] use = call:nova.api.openstack.urlmap:urlmap_factory /: oscomputeversions /v2: openstack_compute_api_v2
osapi_compute是调用urlmap_factory函数返回的一个nova.api.openstack.urlmap.URLMap实 例,nova.api.openstack.urlmap.URLMap继承paste.urlmap.URLMap,它提供了wsgi调用接口,所以该 实例为wsgi app。但是函数nova.api.openstack.urlmap:urlmap_factory与 paste.urlmap.urlmap_factory定义完全一样,不过由于它们所在的module不同,使得它们所用的URLMap分别为与它处于 同一module的URLMap。paste.urlmap.urlmap_factory咋不支持一个传参,来指定URLMap呢?这样nova就不用 重写一样的urlmap_factory了。paste.urlmap.URLMap实现的功能很简单:根据配置将url映射到特定wsgi app,并根据url的长短作一个优先级排序,url较长的将优先进行匹配。所以/v2将先于/进行匹配。URLMap在调用下层的wsgi app前,会更新SCRIPT_NAME和PATH_INFO。nova.api.openstack.urlmap.URLMap继承了 paste.urlmap.URLMap,并写了一堆代码,其实只是为了实现对请求类型的判断,并设置 environ['nova.best_content_type']:如果url的后缀名为json(如/xxxx.json),那么 environ['nova.best_content_type']=“application/json”。如果url没有后缀名,那么将通过 HTTP headers的content_type字段中mimetype判断。否则默认 environ['nova.best_content_type']=“application/json”。
经上面配置加载的osapi_compute为一个URLMap实例,wsgi server的接受的HTTP请求将直接交给该实例处理。它将url为'/v2/.*'的请求将交给 openstack_compute_api_v2,url为'/'的请求交给oscomputerversions处理(它直接返回系统版本号)。其它 的url请求,则返回NotFound。下面继续分析openstack_compute_api_v2,其配置如下:
[composite:openstack_compute_api_v2] use = call:nova.api.auth:pipeline_factory noauth = faultwrap sizelimit noauth ratelimit osapi_compute_app_v2 keystone = faultwrap sizelimit authtoken keystonecontext ratelimit osapi_compute_app_v2keystone_nolimit = faultwrap sizelimit authtoken keystonecontext osapi_compute_app_v2
openstack_compute_api_v2 是调用nova.api.auth.pipeline_factory()返回的wsgi app。pipeline_factory()根据配置项auth_strategy来加载不同的filter和最终的 osapi_compute_app_v2。filter的大概配置如下:
[filter:faultwrap] paste.filter_factory = nova.api.openstack:FaultWrapper.factoryfilter在nova中对应的是nova.wsgi.Middleware,它的定义如下:
1)异常捕获,防止服务内部处理异常导致wsgi server挂掉;
2)限制HTTP请求body大小,对于太大的body,将直接返回BadRequest;
3)对请求keystone对header中token id进行验证;
4)利用headers初始化一个nova.context.RequestContext实例,并赋给req.environ['nova.context'];
5)限制用户的访问速度。
当HTTP请经过上面五个Middlerware处理后,最终交给osapi_compute_app_v2,它是怎么继续处理呢?它的配置如下:
[app:osapi_compute_app_v2] paste.app_factory = nova.api.openstack.compute:APIRouter.factoryosapi_compute_app_v2是调用nova.api.openstack.compute.APIRouter.factory()返回 的一个APIRouter实例。nova.api.openstack.compute.APIRouter继承 nova.api.openstack.APIRouter,nova.api.openstack.APIRouter又继承 nova.wsgi.APIRouter。APIRouter通过A它的成员变量mapper来建立和维护url与controller之间的映射,该 mapper是nova.api.openstack.ProjectMapper的实例,它继承 nova.api.openstack.APIMapper(routes.Mapper)。APIMapper将每个url的format限制为 json或xml,对于其它扩展名的url将返回NotFound。ProjectMapper在每个请求url前面加上一个project_id,这样 每个请求的url都需要带上用户所属的project id,所以一般请求的url为/v2/project_id/resources。 nova.api.openstack.compute.APIRouter.setup_routes代码如下:
参考文献:
[1]
[2]
[3]
[4]