OpenStack源码阅读【2021-10-24】

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() 

对于一个服务,它的启动流程如下:

  1. 初始化一个以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)
    
  2. 启动刚才初始化的服务,设置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
    
  3. started += 1
    # 用于统计正确启动的服务的数量
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值