• nova-api源码分析(WSGI server的创建及启动)


    源码版本:H版

    一、前奏

           nova api本身作为一个WSGI服务器,对外提供HTTP请求服务,对内调用nova的其他模块响应相应的HTTP请求。分为两大部分,一是服务器本身的启动与运行,一是加载的app,这个用来处理请求。

       目录结构如下:

          

          首先,nova api是作为一个WSGI服务,肯定要查看它的启动过程,查看启动脚本/etc/init.d/openstack-nova-api(使用service命令实际在调用/etc/init.d文件夹下的脚本文件)。在/etc/init.d/openstack-nova-api文件中,start选项代表调用脚本/usr/bin/nova-api,如下:

     /usr/bin/nova-api

    import sys
    from nova.cmd.api import main
    if __name__ == "__main__":
      sys.exit(main())

    接着,分析如下:

    /nova/cmd/api.py (文件位置为/usr/lib/python2.6/site-packages,此处及下面均省略)

    def main():
        config.parse_args(sys.argv)
        logging.setup("nova")
        utils.monkey_patch()
    
        launcher = service.process_launcher()
        for api in CONF.enabled_apis:
            should_use_ssl = api in CONF.enabled_ssl_apis
            if api == 'ec2':
                """为了与ec2兼容"""
                server = service.WSGIService(api, use_ssl=should_use_ssl,
                                             max_url_len=16384) 
            else:
                server = service.WSGIService(api, use_ssl=should_use_ssl) 【1
            launcher.launch_service(server, workers=server.workers or 1)【2】
        """当前进程处于等待状态"""
      launcher.wait()

     二、分析【1】处:主要是WSGIService对象的创建

    /nova/service.py

    WSGIService类:
    def __init__(self, name, loader=None, use_ssl=False, max_url_len=None):
        self.name = name
        self.manager = self._get_manager()
        self.loader = loader or wsgi.Loader()
        self.app = self.loader.load_app(name)
        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)
        self.use_ssl = use_ssl
        """封装了wsgi.py中的server"""
        self.server = wsgi.Server(name,
                                  self.app,
                                  host=self.host,
                                  port=self.port,
                                  use_ssl=self.use_ssl,
                                  max_url_len=max_url_len)
        # Pull back actual port used
        self.port = self.server.port
        self.backdoor_port = None

    /nova/wsgi.py

    Loader类:
    def __init__(self, config_path=None):
      """试图寻找api-paste.ini配置文件"""
      self.config_path = None
    
      config_path = config_path or CONF.api_paste_config
      if not os.path.isabs(config_path):
        self.config_path = CONF.find_file(config_path)
      elif os.path.exists(config_path):
        self.config_path = config_path
    
      if not self.config_path:
        raise exception.ConfigNotFound(path=config_path)
    
    def load_app(self, name):
      try:
        LOG.debug("Loading app %(name)s from %(path)s",
                    {'name': name, 'path': self.config_path})
        return deploy.loadapp("config:%s" % self.config_path, name=name)
      except LookupError as err:
        LOG.error(err)
        raise exception.PasteAppNotFound(name=name, path=self.config_path)    

    (此处关于app的具体加载过程见后文。)

    三、分析【2】处:主要是WSGIService的启动

    /nova/openstack/common/service.py

    ProcessLauncher类:
    def launch_service(self, service, workers=1):
      wrap = ServiceWrapper(service, workers)
    
      LOG.info(_('Starting %d workers'), wrap.workers)
      """循环启动workers数目的子进程"""
      while self.running and len(wrap.children) < wrap.workers:
        self._start_child(wrap)
    
    def _start_child(self, wrap):
      ...
      """创建子进程"""
      pid = os.fork()
      if pid == 0:
                
        status = 0
        try:
          """子进程进行处理"""
          self._child_process(wrap.service)
        except SignalExit as exc:
          signame = {signal.SIGTERM: 'SIGTERM',
                        signal.SIGINT: 'SIGINT'}[exc.signo]
          LOG.info(_('Caught %s, exiting'), signame)
          status = exc.code
        except SystemExit as exc:
          status = exc.code
        except BaseException:
          LOG.exception(_('Unhandled exception'))
          status = 2
        finally:
          """出现异常停掉服务"""
          wrap.service.stop()
        """子进程退出"""
        os._exit(status)
    
      LOG.info(_('Started child %d'), pid)
      wrap.children.add(pid)
      self.children[pid] = wrap
      return pid
    
    
    
    def _child_process(self, service):
      ...
      launcher = Launcher()
      launcher.run_service(service)

    /nova/openstack/common/service.py

    Launcher类:
    def __init__(self):
        self._services = threadgroup.ThreadGroup()
        self.backdoor_port = eventlet_backdoor.initialize_if_enabled() 
    
    
    @staticmethod
    def run_service(service):
        service.start()
        service.wait()

    由于这里的service是WSGIService对象,如下:

    /nova/service.py

    WSGIService类:
    def start(self):
    if self.manager: self.manager.init_host() self.manager.pre_start_hook() if self.backdoor_port is not None: self.manager.backdoor_port = self.backdoor_port self.server.start() if self.manager: self.manager.post_start_hook() def wait(self): self.server.wait()

    /nova/wsgi.py

    Server类:
    def start(self):
        ...
        wsgi_kwargs = {
            'func': eventlet.wsgi.server,
            'sock': self._socket,
            'site': self.app,
            'protocol': self._protocol,
            'custom_pool': self._pool,
            'log': self._wsgi_logger,
            'log_format': CONF.wsgi_log_format
            }
    
        if self._max_url_len:
            wsgi_kwargs['url_length_limit'] = self._max_url_len
        """创建一个green thread,运行func函数,在其中传入后面的参数。此处为调用eventlet.wsgi.server函数,传入sock=self._socket等参数到server函数中。其中eventlet.wsgi.server函数负责启动一个wsgi服务器程序接受客户端发来的请求,
    可以参考:http://eventlet.net/doc/modules/wsgi.html?highlight=wsgi.server#eventlet.wsgi.server
    """ self._server = eventlet.spawn(**wsgi_kwargs) def wait(self): try: """green thread阻塞等待""" self._server.wait() except greenlet.GreenletExit: LOG.info(_("WSGI server has stopped."))

    总结:

          我们可以通过pstree命令查看系统中进程的子进程数,发现nova-api进程中有一个主进程,其子进程数目为一个ec2,一个metadata,有workers数目的osapi_compute(其中workers可以通过/etc/nova/nova.conf中osapi_compute_workers选项设置)。然后通过ps –Lf查看每个进程的线程数目,发现其均为单线程。由此可以看出,整个server的创建和启动过程就是,主进程产生若干的子进程,子进程使用green thread启动wsgi server并等待服务。

    参考文档:

    http://www.choudan.net/2013/12/09/OpenStack-WSGI-APP%E5%AD%A6%E4%B9%A0.html

    http://developer.openstack.org/api-ref-compute-v2.html(nova的API文档)

  • 相关阅读:
    一行代码解析复杂JSON文件:利用Android自带的包解析JSON
    人生的真相
    为我的外婆写上我的挽歌
    总结2016 展望2017
    [转贴]使用CryptoAPI解析X509证书和P12证书
    2015这一年的进步
    怎样在WINDOWS下面编译LIBCURL
    LINQ TO SQL 怎样 执行存储过程并返回存储过程的临时表
    C调用OPENSSL做REST服务客户端的例子
    一个CLI的 的例子
  • 原文地址:https://www.cnblogs.com/littlebugfish/p/4022907.html
Copyright © 2020-2023  润新知