Openstack liberty 中Cinder-api启动过程源码分析1

本文深入分析OpenStack Liberty版本中Cinder-api服务的启动过程,涵盖请求路由映射、WSGI应用发现和服务器初始化。通过理解Cinder-api的启动流程,包括加载应用、创建服务对象和构建路由,为后续的源码分析奠定基础。
摘要由CSDN通过智能技术生成

在前面的博文中,主要分析了GlanceNova相关的代码,从这篇文章开始我将转到Cinder的源码分析上来。Cinder模块在Openstack中为云主机提供块存储,主要包含:cinder-api,cinder-scheduler,cinder-volumecinder-backup4个部分,后续将通过一系列文章逐个分析各个组件的源码。

今天先来看看cinder-api启动过程的源码分析,预计将包括如下几个方面的内容:

  • 请求路由映射(Python Routes)
  • WSGI 应用发现(Python Paste Deployment)
  • WSGI服务器

限于篇幅,可能将上述主题拆分到多篇博文,下面一起来看具体内容:

启动cinder-api服务

当你通过cinder-api命令(如:/usr/bin/cinder-api --config-file /etc/cinder/cinder.conf)启动api服务时,执行的实际上是
cinder/cmd/api.py/main()函数, 如下:

#`cinder/cmd/api.py/main`
def main():
    """省略次要代码,完成代码请查看相关文件"""

    #加载辅助对象,封装与数据库相关的操作
    objects.register_all()

    #加载配置并设置日志
    CONF(sys.argv[1:], project='cinder',
         version=version.version_string())
    logging.setup(CONF, "cinder")

    """初始化rpc:
    设置全局Transport和Notifier,Transport是
    oslo_messaging/transport.py/Transport实例,我采用的是默认的
    rpc_backend=rabbit,所以Transport采用的driver=oslo_messaging/
    _drivers/impl_rabbit.py/RabbitDriver;Notifier是一个通知消息发
    送器,它借助Transport将通知发送发送给ceilometer
    """
    rpc.init(CONF)

    #通过服务启动器启动WSGI服务(`osapi_volume`)并等待服务启动成功
    #在初始化WSGI服务时,会设置路由映射以及加载WSGI应用程序
    #在启动WSGI服务时,会启动http监听

    #下文具体分析相关内容
    launcher = service.process_launcher()
    server = service.WSGIService('osapi_volume')
    launcher.launch_service(server, workers=server.workers)
    launcher.wait()

创建WSGIService服务对象

def main():

    ......

    #创建一个名为`osapi_volume`的`WSGIService`服务对象
    server = service.WSGIService('osapi_volume')

    ......

#接上文,一起来看看`WSGIService`服务对象的初始化函数
#`cinder/service.py/WSGIService.__init__`
def __init__(self, name, loader=None):

    """Initialize, but do not start the WSGI server."""

    #服务名`osapi_volume`
    self.name = name
    #加载名为(`osapi_volume_manager`)的管理器(None)
    self.manager = self._get_manager()
    """创建WSGI应用加载器(`cinder/wsgi/common.py/Loader`)
    并根据配置文件(`cinder.conf`)设置应用配置路径:
    `config_path` = `/etc/cinder/paste-api.ini`
    """
    self.loader = loader or wsgi_common.Loader()

    """加载WSGI应用并设置路由映射
    return paste.urlmap.URLMap, 请看后文的具体分析
    """
    self.app = self.loader.load_app(name)

    """根据配置文件(`cinder.conf`)设置监听地址及工作线程数
    如果未指定监听ip及端口就分别设置为`0.0.0.0`及`0`
    如果为指定工作线程数就设置为cpu个数
    如果设置的工作线程数小于1,则抛异常
    """
    self.host = getattr(CONF, '%s_listen' % name, "0.0.0.0")
    self.port = getattr(CONF, '%s_listen_port' % name, 0)
    self.workers = (getattr(CONF, '%s_workers' % name, None) or
                        processutils.get_worker_count())
    if self.workers and self.workers < 1:
        worker_name = '%s_workers' % name
        msg = (_("%(worker_name)s value of %(workers)d is" 
                    "invalid, must be greater than 0.") %
                    {
  'worker_name': worker_name,
                     'workers': self.workers})
        raise exception.InvalidInput(msg)

    """如果CONF.profiler.profiler_enabled = True就开启性能分析  
    创建一个类型为`Messaging`的通知器(`_notifier`),将性能数据发送给
    ceilometer
    """
    setup_profiler(name, self.host)

    #创建WSGI服务器对象(`cinder/wsgi/eventlet_server.py/Server`)
    #下一篇博文再具体分析WSGI服务器的初始化及启动过程,敬请期待!!!
    self.server = wsgi.Server(name,
                              self.app,
                              host=self.host,
                              port=self.port)

小结:在初始化WSGIService服务对象过程中,主要完成了如下操作:

  • 加载WSGI ApplicationPython Paste Deployment
  • 设置路由映射(Python Routes
  • 创建WSGI服务器对象并完成初始化

先来看WSGI Application的加载过程:

加载WSGI应用

上文的self.loader.load_app(name),执行的是如下的调用:

#`cinder/wsgi/common.py/Loader.load_app`
def load_app(self, name):
    """Return the paste URLMap wrapped WSGI application.

    `Python Paste`系统可以用来发现以及配置`WSGI`应用及服务, 包含如下三
    种调用入口:

               `loadapp`    `loadfilter`   `loadserver`
                  |              |               |      
                         |               |
                                 |
                                 V
                             `loadobj`
                                 |
                                 V
                            `loadcontext` 
                                 |
                        |                |
                 |               |               |
                 V               V               V
           _loadconfig       _loadegg         _loadfunc

    分别用来配置`WSGI App`,`WSGI Filter`,`WSGI Server`;
    `loadcontext`方法基于配置文件类型(`config`,`egg`,`call`),调用具
    体的配置方法,在我们的示例中是:`loadapp` -> `loadobj` -> 
    `loadcontext` -> `_loadconfig`,下文依次分析:
    """

    try:
        #从`self.config_path`(`/etc/cinder/api-paste.ini`)指定的
        #配置中加载名为`name`(`osapi_volume`)的应用
        return deploy.loadapp("config:%s" % self.config_path, 
                                                    name=name)
    except LookupError:
        LOG.exception(_LE("Error loading app %s"), name)
        raise exception.PasteAppNotFound(name=name, path=self.config_path)

#接上文,直接通过`Python Paste`系统配置`WSGI`应用
#`../site-packages/paste/deploy/loadwsgi.py/loadapp`
def loadapp(uri, name=None, **kw):
    """输入参数如下:
    uri: 'config:/etc/cinder/api-paste.ini'
    name: 'osapi_volume'
    **kw: None
    """

    """APP = _APP(),是一个_APP实例对象,定义应用所支持的协议及其前缀:
    APP.name = 'application'
    APP.egg_protocols = [['paste.app_factory'], 
                         ['paste.composite_factory'],
                         ['paste.composit_factory']]
    APP.config_prefixes = [['app', 'application'],
                           ['composite', 'composit'],
                           ['pipeline], 
                           ['filter-app']]

    在后文的分析中会根据应用的协议来生成上下文(`context`)
    """
    return loadobj(APP, uri, name=name, **kw)

#接上文`loadobj`
def loadobj(object_type, uri, name=None, relative_to=None,
            global_conf=None):
    """根据应用的协议类型生成上下文并执行

    object_type: _APP对象
    uri: 'config:/etc/cinder/api-paste.ini'
    name: 'osapi_volume'
    """
    context = loadcontext(
        object_type, uri, name=name, relative_to=relative_to,
        global_conf=global_conf)
    return context.create()

#接上文:这是一个工厂方法,它根据uri中的配置文件类型
#(`config`,`egg`,`call`)分别调用具体的配置方法
#(`_loadconfig`,`_loadegg`, `_loadfunc`)
def loadcontext(object_type, uri, name=None, relative_to=None,
                global_conf=None):
    """创建应用上下文,结合输入参数,代码逻辑就很好理解了

    object_type: _APP对象
    uri: 'config:/etc/cinder/api-paste.ini'
    name: 'osapi_volume'
    relative_to: None
    global_conf: None
    """           
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值