首页 > 代码库 > nova api源码分析(一)

nova api源码分析(一)

说明:

源码版本:H版

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

 

一、前奏

       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 sysfrom nova.cmd.api import mainif __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的具体加载过程见"nova api源码分析(二)"

 

三、分析【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 piddef _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() @staticmethoddef 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并等待服务。

nova api源码分析(一)