您的位置:首页 > 运维架构

openstack nova-api启动流程

2016-04-20 14:53 267 查看

一、预备知识

    nova-api服务启动过程比较简单,难点在于它用到的一些库不了解,建议先了解一下python paste.deploy,routes,webob这些这些模块。

    paste.deploy
       主要是通过解析/etc/nova/api-paste.ini,根据指定的name来加载wsgi app。

    webob

        WebOb是一个用于对WSGI request环境进行包装以及用于创建WSGI
response的一个包。简化了wsgi app的编写。

    routes

        定义请求到具体函数的映射

nova-api启动流程

    在/usr/bin下有个nova-api脚本,这是整个过程的入口。我们可以将它的内容打印出来看看
    
import sys

from nova.cmd.api import main

if __name__=="__main__":
sys.exit(main())
    可以看到,执行的是nova/cmd/api.py中mian。我们再去看看api.py的main函数
def main():
config.parse_args(sys.argv)
logging.setup(CONF, "nova")
utils.monkey_patch()
objects.register_all()

gmr.TextGuruMeditation.setup_autorun(version)

launcher = service.process_launcher()
for api in CONF.enabled_apis:
should_use_ssl = api in CONF.enabled_ssl_apis
if api == 'ec2':
server = service.WSGIService(api, use_ssl=should_use_ssl,
max_url_len=16384)
else:
server = service.WSGIService(api, use_ssl=should_use_ssl)
launcher.launch_service(server, workers=server.workers or 1)
launcher.wait()
    首先是解析参数:
config.parse_args(sys.argv)
    然后设置日志
logging.setup(CONF, "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':
server = service.WSGIService(api, use_ssl=should_use_ssl,
max_url_len=16384)
else:
server = service.WSGIService(api, use_ssl=should_use_ssl)
launcher.launch_service(server, workers=server.workers or 1)
来看看service.WSGIService到底是什么东西,贴出部分WSGIService代码
class WSGIService(object):
"""Provides ability to launch API from a 'paste' configuration."""

def __init__(self, name, loader=None, use_ssl=False, max_url_len=None):
"""Initialize, but do not start the WSGI server.

:param name: The name of the WSGI server given to the loader.
:param loader: Loads the WSGI application using the given name.
:returns: None

"""
self.name = name
self.manager = self._get_manager()
self.loader = loader or wsgi.Loader()
self.app = self.loader.load_app(name)
# inherit all compute_api worker counts from osapi_compute
if name.startswith('openstack_compute_api'):
wname = 'osapi_compute'
else:
wname = 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' % wname, 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)s is invalid, "
"must be greater than 0") %
{'worker_name': worker_name,
'workers': str(self.workers)})
raise exception.InvalidInput(msg)
self.use_ssl = use_ssl
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
    """Provides ability to launch API from a 'paste' configuration."""
    意思是通过paste配置来生成WSGI的app。
    着重看一下这里面的loader,这是用来启动app的。
self.loader = loader or wsgi.Loader()
    看一下nova/wsgi.py中的Loader,通过paste配置来启动wsgi app
class Loader(object):
"""Used to load WSGI applications from paste configurations."""

def __init__(self, config_path=None):
"""Initialize the loader, and attempt to find the config.

:param config_path: Full or relative path to the paste config.
:returns: None

"""
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):
"""Return the paste URLMap wrapped WSGI application.

:param name: Name of the application to load.
:returns: Paste URLMap object wrapping the requested application.
:raises: `nova.exception.PasteAppNotFound`

"""
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:
LOG.exception(_LE("Couldn't lookup app: %s"), name)
raise exception.PasteAppNotFound(name=name, path=self.config_path)
    可以看到load_app是通过deploy.loadapp()来启动wsgi app
    然后是:
launcher.launch_service(server, workers=server.workers or 1)
    看看launch_service()是什么
def launch_service(self, service, workers=1):
wrap = ServiceWrapper(service, workers)

LOG.info(_LI('Starting %d workers'), wrap.workers)
while self.running and len(wrap.children) < wrap.workers:
self._start_child(wrap)
    ServiceWrapper是一类,类似于C语言中结构体的作用
class ServiceWrapper(object):
def __init__(self, service, workers):
self.service = service
self.workers = workers
self.children = set()
self.forktimes = []

def _start_child(self, wrap):
if len(wrap.forktimes) > wrap.workers:
# Limit ourselves to one process a second (over the period of
# number of workers * 1 second). This will allow workers to
# start up quickly but ensure we don't fork off children that
# die instantly too quickly.
if time.time() - wrap.forktimes[0] < wrap.workers:
LOG.info(_LI('Forking too fast, sleeping'))
time.sleep(1)

wrap.forktimes.pop(0)

wrap.forktimes.append(time.time())

pid = os.fork()
if pid == 0:
launcher = self._child_process(wrap.service)
while True:
self._child_process_handle_signal()
status, signo = self._child_wait_for_exit_or_signal(launcher)
if not _is_sighup_and_daemon(signo):
break
launcher.restart()

os._exit(status)

LOG.info(_LI('Started child %d'), pid)

wrap.children.add(pid)
self.children[pid] = wrap

return pid
    首先,os.fork()返回值可能为0,表示子进程本身,或者大于0表示父进程。
    在这里,父进程主要是创建子进程,更新warp信息,返回子进程的pid。
    当pid=0时表示子进程,子进程的话先通过launcher
= self._child_process(wrap.service)启动对应的服务,然后就是while循环等待信号。如果有终止的信号量就结束,否则则重新启动进程。

 
  最后是    
launcher.wait()
 
  它的注释是:"""Loop waiting on children to die and respawning as necessary."""。它的作用是循环监听子进程是否挂掉,如果挂掉则重启。如果收到终止的信号,则kill所有的children然后结束自己。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: