2021SC@SDUSC
本期代码聚焦:nova的启动流程
nova-api启动代码:(nova/cmd/api.py)
def main():
# =======做一些常规操作,如解析参数,设置logger,注册对象,开版本缓存,初始化报告等==========
config.parse_args(sys.argv)
logging.setup(CONF, "nova")
objects.register_all()
gmr_opts.set_defaults(CONF)
if 'osapi_compute' in CONF.enabled_apis:
# NOTE(mriedem): This is needed for caching the nova-compute service version.
objects.Service.enable_min_version_cache()
log = logging.getLogger(__name__)
gmr.TextGuruMeditation.setup_autorun(version, conf=CONF)
# ===============开启CONF.enabled_apis中的每一个服务(具体操作见下)===================
launcher = service.process_launcher()
started = 0
for api in CONF.enabled_apis: # enabled_apis = ec2,osapi_compute,metadata
should_use_ssl = api in CONF.enabled_ssl_apis
try:
server = service.WSGIService(api, use_ssl=should_use_ssl)
launcher.launch_service(server, workers=server.workers or 1)
started += 1
except exception.PasteAppNotFound as ex:
log.warning("%s. ``enabled_apis`` includes bad values. "
"Fix to remove this warning.", ex)
if started == 0:
log.error('No APIs were started. '
'Check the enabled_apis config option.')
sys.exit(1)
# ======让api服务处于等待外部请求状态===========
launcher.wait()
对于一个服务,它的启动流程如下:
-
初始化一个以api变量值为名称的WSGIService服务(但先不启动它)。从上文可知,api是CONF.enabled_apis中的一个。
# nova/cmd/api.py server = service.WSGIService(api, use_ssl=should_use_ssl)
WSGIService继承自service.Service,初始化service的时候,用参数和CONF中的属性值实例化对象属性,其中,name、app、host、port、use_ssl、max_url_len用于初始化基类Server,获取server对象,并用返回的server对象的port属性值初始化当前类对象的port值,用name和host设置一个profiler。
# nova/service.py/WSGIService def __init__(self, name, loader=None, use_ssl=False, max_url_len=None): self.name = name # NOTE(danms): Name can be metadata, osapi_compute, per # nova.service's enabled_apis self.binary = 'nova-%s' % name LOG.warning('Running %s using eventlet is deprecated. Deploy with ' 'a WSGI server such as uwsgi or mod_wsgi.', self.binary) self.topic = None self.manager = self._get_manager() self.loader = loader or api_wsgi.Loader() # 通过CONF指定参数 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) # wsgi.Server会将监听到的http请求交给app处理 # Pull back actual port used self.port = self.server.port self.backdoor_port = None setup_profiler(name, self.host)
-
启动刚才初始化的服务,设置worker数量最少为1
# nova/cmd/api.py launcher.launch_service(server, workers=server.workers or 1)
launch_service先检查传入的service是否是一个合格的service(即是ServiceBase这个大基类的实例)
# Python38\Lib\site-packages\oslo_service\service.py def _check_service_base(service): if not isinstance(service, ServiceBase): raise TypeError(_("Service %(service)s must an instance of %(base)s!") % {'service': service, 'base': ServiceBase})
然后用service和workers实例化一个ServiceWrapper:
# Python38\Lib\site-packages\oslo_service\service.py class ServiceWrapper(object): def __init__(self, service, workers): self.service = service self.workers = workers self.children = set() # set()函数创建一个无序不重复元素集,可进行关系测试,删除重复数据,还可以计算交集、差集、并集等 self.forktimes = []
然后设置不让垃圾回收机制回收已经存在的实例:
# Python38\Lib\site-packages\oslo_service\service.py if hasattr(gc, 'freeze'): gc.freeze()
最后fork workers数量的进程,并保存好pid:
# Python38\Lib\site-packages\oslo_service\service.py while self.running and len(wrap.children) < wrap.workers: self._start_child(wrap)
# Python38\Lib\site-packages\oslo_service\service.py 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('Forking too fast, sleeping') time.sleep(1) wrap.forktimes.pop(0) wrap.forktimes.append(time.time()) pid = os.fork() if pid == 0: self.launcher = self._child_process(wrap.service) while True: self._child_process_handle_signal() status, signo = self._child_wait_for_exit_or_signal( self.launcher) if not _is_sighup_and_daemon(signo): self.launcher.wait() break self.launcher.restart() os._exit(status) LOG.debug('Started child %d', pid) wrap.children.add(pid) self.children[pid] = wrap return pid
-
started += 1 # 用于统计正确启动的服务的数量