Openstack中的路由

一、服务的加载及初始化

osapi-karbor 服务启动的过程中,调用 deploy.loadpp 使用 config 方式从 api-paste.ini 文件来 load 名为osapi_karbor 的应用,其入口在文件的 [composite:osapi_karbor]部分:

[composite:osapi_karbor]
use = egg:Paste#urlmap
/: apiversions
/v1: openstack_karbor_api_v1

配置文件中的osapi_karbor 即 deploy.loadapp方法中传入的name。
注:配置文件中的use字段,可以通过不同的场景,动态加载不同的api,比如可以自定义一个方法,通过开关初始化urlmap并返回。

加载 openstack_karbor_api_v1,它对应的composite 是:

[composite:openstack_karbor_api_v1]
use = call:karbor.api.middleware.auth:pipeline_factory
noauth = request_id catch_errors noauth access_log apiv1
keystone = request_id catch_errors cloud_trace_wrap faultwrap authtoken access_log keystonecontext apiv1

karbor/api/middleware/auth.py 中的 pipeline_factory 方法如下:

def pipeline_factory(loader, global_conf, **local_conf):
    """A paste pipeline replica that keys off of auth_strategy."""
    pipeline = local_conf[CONF.auth_strategy]
    pipeline = pipeline.split()
    filters = [loader.get_filter(n) for n in pipeline[:-1]]
    app = loader.get_app(pipeline[-1])
    filters.reverse()
    for filter in filters:
        app = filter(app)
    return app

它首先会读取 karbor.conf 中 auth_strategy 的值。我们一般会使用 token,所以它会从 local_conf 中读取keystone 的 pipeline 。

第一步,loader.get_filter(filter)来使用 deploy loader 来 load 每一个 filter,它会调用每个 filter 对应的入口 factory 函数,该函数在不同的 MiddleWare 基类中定义,都是返回函数 _factory 的指针(keystonemiddleware 是 函数auth_filter的指针)。
第二步,app = loader.get_app(pipeline[-1]):使用 deploy loader 来 load app apiv1,调用入口函数 kangaroo.api.router: DJRouter.factory,factory 函数会调用其__init__方法,该方法里面进行了相关url跟controller的映射配置。
第三步,app = filter(app):然后调用每个 filter 来 wrapper APIRouter。
依次调用 filter 类的 factory,传入 app 做为参数。

到这里,osapi_karbor 的 loading 就结束了。在此过程中,各个 filter middleware 以及 WSGI Application 都被加载/注册,并被初始化。WSGI Server 在被启动后,开始等待 HTTP request,然后进入HTTP request 处理过程。

二、APIRouter

APIRouter 是 karbor 中的核心类之一,它负责分发 HTTP Request 到其管理的某个 Resource。它继承自oslo_service/wsgi.py中的Router类
在处理 HTTP Request 阶段,它负责接收经过 Middleware filters 处理过的 HTTP Request,再把它转发给 RoutesMiddleware 实例

class Router(object):
       … 
	self.map = mapper
        self._router = routes.middleware.RoutesMiddleware(self._dispatch,
                                                          self.map)

RoutesMiddlewar会使用 mapper 得到 Request 的URL 对应的 controller 和 action,并设置到 Request 的 environ 中,然后再调用self.app()对象实例。
注:这里涉及到python中的可调用对象概念。

该self.app是从Router中传入的:

def _dispatch(req):
    …
    match = req.environ['wsgiorg.routing_args'][1]
    if not match:
        return webob.exc.HTTPNotFound()
    app = match['controller']
    return app

该方法会从 environ 中得到controller,其实是个 Resource 的实例,然后调用其 _call 方法,实现消息的分发。

比如karbor中的task任务为例
在DJRouter中,在mapper中定义了task的相关映射

class DJRouter(APIRouter):
    @classmethod
    def factory(cls, global_conf, **local_conf):
        return cls(ProjectMapper())

    def __init__(self, mapper):
        client_factory.init()
        dj_task_resource = task.create_resource()
        …
        mapper.connect("dj_task",
                       "/{project_id}/providers/{provider_id}/tasks",
                       controller=dj_task_resource,
                       action='list',
                       conditions={"method": ['GET']})

进入/api/task.py中,有如下create_resource()方法

def create_resource():
    return wsgi.Resource(TaskController())
上一篇:代理java


下一篇:设计模式概览